mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-27 11:13:02 +02:00
Compare commits
194
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eec5546100 | ||
|
|
4451e70ba8 | ||
|
|
1f29bfa5a6 | ||
|
|
aea6ef3961 | ||
|
|
1e4a88e0b9 | ||
|
|
ba25914cbe | ||
|
|
84391c18a4 | ||
|
|
c73b3b59c2 | ||
|
|
b3e6a38d76 | ||
|
|
9847a8fdd1 | ||
|
|
87bffb3816 | ||
|
|
8b44a42424 | ||
|
|
2e408d2ee1 | ||
|
|
300734872c | ||
|
|
6b91a6765c | ||
|
|
9ab8bd5bc9 | ||
|
|
955de32ea8 | ||
|
|
b449b7ea82 | ||
|
|
f0c23ca048 | ||
|
|
c6626b6520 | ||
|
|
9c203f614b | ||
|
|
1df1df7096 | ||
|
|
92744b7318 | ||
|
|
be7b434d74 | ||
|
|
a47e8b6d4a | ||
|
|
b004f081a1 | ||
|
|
06365fa3da | ||
|
|
75b47b8a7a | ||
|
|
aa039449da | ||
|
|
cd7412aea4 | ||
|
|
2564d2597b | ||
|
|
cb1054e4a3 | ||
|
|
24549d149c | ||
|
|
2bf6ec7d1a | ||
|
|
ac97ce01a5 | ||
|
|
a730cc0439 | ||
|
|
3c8879cbdf | ||
|
|
c9dc5b928a | ||
|
|
a9458f79b1 | ||
|
|
1b1ca208e1 | ||
|
|
5ee342849d | ||
|
|
2bcb02d3ef | ||
|
|
c1787622a1 | ||
|
|
e0f97b1840 | ||
|
|
d1b9f1fb45 | ||
|
|
e77772e8c5 | ||
|
|
c6d9c68726 | ||
|
|
103398f070 | ||
|
|
2f844f9ac7 | ||
|
|
27a4278cce | ||
|
|
3a04bec92d | ||
|
|
9aa9a60aca | ||
|
|
954d26a7e2 | ||
|
|
4b2acdedc5 | ||
|
|
0741d2a655 | ||
|
|
e38063c0ac | ||
|
|
a2bc3bef61 | ||
|
|
eceeb8c59f | ||
|
|
d12d66b013 | ||
|
|
e73742504d | ||
|
|
6cde353029 | ||
|
|
a9e78aa329 | ||
|
|
7a52f49c7d | ||
|
|
ba76103c7f | ||
|
|
b0b1a79aa5 | ||
|
|
5762a73c45 | ||
|
|
4909a9e553 | ||
|
|
83821def3b | ||
|
|
f1ab05a86f | ||
|
|
412ee33f09 | ||
|
|
0f3433e9a0 | ||
|
|
807b113d4e | ||
|
|
5f25c063d5 | ||
|
|
5038cd47e5 | ||
|
|
5386ec9a3a | ||
|
|
00c2c0c608 | ||
|
|
4856705803 | ||
|
|
a38f5902c8 | ||
|
|
ec4c1b1d70 | ||
|
|
bdbbfcb50f | ||
|
|
f8b4a858bc | ||
|
|
3da045dd40 | ||
|
|
72a86301f0 | ||
|
|
fcc9fe84f4 | ||
|
|
9ce763662f | ||
|
|
257636d997 | ||
|
|
25c31ce218 | ||
|
|
bcdd311cb8 | ||
|
|
3159d6c042 | ||
|
|
8354997104 | ||
|
|
b0a0b31e68 | ||
|
|
686f0bec03 | ||
|
|
360d3b322d | ||
|
|
6fa1eb9e00 | ||
|
|
a530db9ae5 | ||
|
|
337f39a74d | ||
|
|
3b5c0248d7 | ||
|
|
168fb24957 | ||
|
|
ec3e3b3322 | ||
|
|
67bb7e5928 | ||
|
|
214ca82bd6 | ||
|
|
a05a3609c5 | ||
|
|
e4aa36dfe7 | ||
|
|
34b06fa448 | ||
|
|
81d8643a6f | ||
|
|
8edf238b68 | ||
|
|
61cba2615f | ||
|
|
a6f6fcac6a | ||
|
|
8ea159ce5d | ||
|
|
235d3e46e6 | ||
|
|
a9a7b7016d | ||
|
|
ed86a29bfc | ||
|
|
580599f549 | ||
|
|
b7d119b23f | ||
|
|
ff32314676 | ||
|
|
cd4e8b0a9f | ||
|
|
31502c8aab | ||
|
|
aafa88b648 | ||
|
|
414703e851 | ||
|
|
256c462c0c | ||
|
|
679a6671ed | ||
|
|
d8dde9cde2 | ||
|
|
f1bf02eda5 | ||
|
|
aa7ea66a0d | ||
|
|
7a57d4f0fa | ||
|
|
3d85eba3fc | ||
|
|
b04c81199d | ||
|
|
ffd2128614 | ||
|
|
2b19b61068 | ||
|
|
0f0c119553 | ||
|
|
5fdc408ade | ||
|
|
9e801dfa2f | ||
|
|
0bda0727b7 | ||
|
|
0b6f7a6b93 | ||
|
|
ad0463e96c | ||
|
|
3bdf360b2e | ||
|
|
58ebae5624 | ||
|
|
b3e418c6a7 | ||
|
|
f0f0e737a9 | ||
|
|
84f83d635b | ||
|
|
e5dd01ffcc | ||
|
|
4f98b0b761 | ||
|
|
074754d42d | ||
|
|
51dc940059 | ||
|
|
4793cf0770 | ||
|
|
0468f35fb6 | ||
|
|
1e87a30147 | ||
|
|
83effa6fd4 | ||
|
|
8f996859dc | ||
|
|
4869b73b14 | ||
|
|
c1309da10b | ||
|
|
acc39b1b15 | ||
|
|
ddd0fc5a35 | ||
|
|
afa243cd2f | ||
|
|
b66b56f504 | ||
|
|
d4a1a7c91c | ||
|
|
c57a4655b8 | ||
|
|
30e2a814eb | ||
|
|
caea3132c9 | ||
|
|
746392cac5 | ||
|
|
5afaeae46c | ||
|
|
15b32cfcc2 | ||
|
|
8e0db4c7e4 | ||
|
|
420e0d5b36 | ||
|
|
69eae34203 | ||
|
|
c0a071e87a | ||
|
|
2011f5cbf2 | ||
|
|
a6ff416401 | ||
|
|
66c20aa79d | ||
|
|
b0f2388153 | ||
|
|
5e707753e9 | ||
|
|
6099ac79e2 | ||
|
|
6f41ea5ccb | ||
|
|
c58bd5ead4 | ||
|
|
bd9acd003d | ||
|
|
0cb1459f43 | ||
|
|
1827b65182 | ||
|
|
1807306178 | ||
|
|
475a3a2919 | ||
|
|
0937921cbc | ||
|
|
6ad80c80ab | ||
|
|
4150f45cc4 | ||
|
|
1da00a9764 | ||
|
|
015215073a | ||
|
|
d73d5440c5 | ||
|
|
104a70d46c | ||
|
|
795f2615cb | ||
|
|
debcf81a7a | ||
|
|
ee50a7c061 | ||
|
|
f35f176860 | ||
|
|
198be2a9c6 | ||
|
|
79ce152abb | ||
|
|
870d41644f | ||
|
|
43017cc79f |
@@ -0,0 +1,37 @@
|
||||
<!--- **Summarize** your changes in the title above -->
|
||||
|
||||
## Description
|
||||
|
||||
<!--
|
||||
-- A description of changes, detailing *why* changes are made.
|
||||
-- Remember: assign a reviewer, or use @mentions if org. member.
|
||||
-->
|
||||
|
||||
|
||||
## Other information
|
||||
|
||||
<!-- Other relevant info, e.g., before/after screenshots -->
|
||||
|
||||
|
||||
## Checklist
|
||||
|
||||
Tick relevant boxes, this PR is-a or has-a:
|
||||
|
||||
- [ ] Bugfix
|
||||
- [ ] Regression tests
|
||||
- [ ] ChangeLog updates (for next release)
|
||||
- [ ] Feature
|
||||
- [ ] YANG model change => revision updated?
|
||||
- [ ] Regression tests added?
|
||||
- [ ] ChangeLog updates (for next release)
|
||||
- [ ] Documentation added?
|
||||
- [ ] Code style update (formatting, renaming)
|
||||
- [ ] Refactoring (please detail in commit messages)
|
||||
- [ ] Build related changes
|
||||
- [ ] Documentation content changes
|
||||
- [ ] Other (please describe):
|
||||
|
||||
|
||||
## References
|
||||
|
||||
<!-- Please list references to related issue(s) -->
|
||||
@@ -16,6 +16,11 @@ jobs:
|
||||
variant: [netconf, classic]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Maintenance
|
||||
run: |
|
||||
docker image prune -af
|
||||
docker volume prune -f
|
||||
docker container prune -f
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set Build Variables
|
||||
id: vars
|
||||
@@ -27,22 +32,25 @@ jobs:
|
||||
fi
|
||||
echo "dir=infix-$target" >> $GITHUB_OUTPUT
|
||||
echo "tgz=infix-$target.tar.gz" >> $GITHUB_OUTPUT
|
||||
if [ "$target" = x86_64 ]; then
|
||||
echo "out=$PWD/output" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "out=/mnt/x-$target" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
- name: Restore Cache of dl/
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: dl/
|
||||
key: dl-${{ matrix.platform }}-${{ matrix.variant }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'configs/*', 'package/*/*.hash') }}
|
||||
key: dl-${{ matrix.variant }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'configs/*', 'package/*/*.hash') }}
|
||||
restore-keys: |
|
||||
dl-${{ matrix.platform }}-${{ matrix.variant }}-
|
||||
dl-${{ matrix.platform }}-
|
||||
dl-${{ matrix.variant }}-
|
||||
dl-
|
||||
- name: Restore Cache of .ccache/
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .ccache/
|
||||
key: ccache-${{ matrix.platform }}-${{ matrix.variant }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'package/*/*.hash') }}
|
||||
key: ccache-${{ matrix.platform }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'package/*/*.hash') }}
|
||||
restore-keys: |
|
||||
ccache-${{ matrix.platform }}-${{ matrix.variant }}-
|
||||
ccache-${{ matrix.platform }}-
|
||||
ccache-
|
||||
- name: Configure & Build
|
||||
@@ -52,22 +60,26 @@ jobs:
|
||||
else
|
||||
target=${{ matrix.platform }}_${{ matrix.variant }}_defconfig
|
||||
fi
|
||||
echo "Buildring $target ..."
|
||||
echo "Building $target ..."
|
||||
sudo mkdir ${{ steps.vars.outputs.out }}
|
||||
sudo chown $(id -un):$(id -gn) ${{ steps.vars.outputs.out }}
|
||||
export O=${{ steps.vars.outputs.out }}
|
||||
make $target
|
||||
make
|
||||
- name: Prepare Artifact
|
||||
run: |
|
||||
cd output
|
||||
cd ${{ steps.vars.outputs.out }}
|
||||
mv images ${{ steps.vars.outputs.dir }}
|
||||
ln -s ${{ steps.vars.outputs.dir }} images
|
||||
tar chfz ${{ steps.vars.outputs.tgz }} ${{ steps.vars.outputs.dir }}
|
||||
- name: Test
|
||||
if: matrix.platform == 'x86_64' && matrix.variant == 'netconf'
|
||||
run: |
|
||||
export O=${{ steps.vars.outputs.out }}
|
||||
make test-qeneth
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: output/${{ steps.vars.outputs.tgz }}
|
||||
path: ${{ steps.vars.outputs.out }}/${{ steps.vars.outputs.tgz }}
|
||||
name: artifact-${{ matrix.variant }}-${{ matrix.platform }}
|
||||
release:
|
||||
if: ${{github.repository_owner == 'kernelkit' && github.ref_name == 'main'}}
|
||||
@@ -90,7 +102,7 @@ jobs:
|
||||
prerelease: true
|
||||
tag: "latest"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
artifacts: "artifact/*"
|
||||
artifacts: "*.tar.gz"
|
||||
- name: Summary
|
||||
run: |
|
||||
cat <<EOF >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
@@ -50,8 +50,9 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install pkg-config libjansson-dev libev-dev libite-dev \
|
||||
libaugeas-dev libglib2.0-dev libpcre2-dev
|
||||
sudo apt-get -y install pkg-config libjansson-dev libev-dev \
|
||||
libaugeas-dev libglib2.0-dev libpcre2-dev \
|
||||
libuev-dev libite-dev
|
||||
- name: Build dependencies
|
||||
run: |
|
||||
git clone https://github.com/CESNET/libyang.git
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
name: Manny the Manager
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
checkout:
|
||||
required: true
|
||||
type: boolean
|
||||
cleanup:
|
||||
required: true
|
||||
type: boolean
|
||||
peek:
|
||||
required: true
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
inventory:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Disk usage ...
|
||||
run: |
|
||||
cd
|
||||
du -hs .[^.]*
|
||||
- name: Disk inventory (1/2) ...
|
||||
run: |
|
||||
echo "df -h ========================================================================="
|
||||
df -h
|
||||
echo "mounts ========================================================================"
|
||||
mount
|
||||
- name: File inventory (1/2) ...
|
||||
run: |
|
||||
echo "Current directory: $(pwd)"
|
||||
echo "Files in $HOME ================================================================"
|
||||
ls $HOME
|
||||
echo "Find $HOME ===================================================================="
|
||||
find $HOME
|
||||
- name: Container inventory ...
|
||||
run: |
|
||||
echo "Available container images: ==================================================="
|
||||
docker images
|
||||
echo "Available containers: ========================================================="
|
||||
docker ps -a
|
||||
|
||||
checkout:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Restore Cache of dl/
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: dl/
|
||||
key: dl-netconf-${{ hashFiles('.git/modules/buildroot/HEAD', 'configs/*', 'package/*/*.hash') }}
|
||||
restore-keys: |
|
||||
dl-netconf-
|
||||
dl-
|
||||
- name: Restore Cache of .ccache/
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .ccache/
|
||||
key: ccache-x86_64-${{ hashFiles('.git/modules/buildroot/HEAD', 'package/*/*.hash') }}
|
||||
restore-keys: |
|
||||
ccache-x86_64-
|
||||
ccache-
|
||||
- name: Disk inventory (2/2) ...
|
||||
run: |
|
||||
echo "df -h ========================================================================="
|
||||
df -h
|
||||
echo "mounts ========================================================================"
|
||||
mount
|
||||
- name: File inventory (2/2) ...
|
||||
run: |
|
||||
echo "Current directory: $(pwd)"
|
||||
echo "Files in $HOME ================================================================"
|
||||
ls $HOME
|
||||
echo "Find $HOME ===================================================================="
|
||||
find $HOME
|
||||
|
||||
peeky:
|
||||
if: ${{ inputs.peek }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Peek & Poke ...
|
||||
run: |
|
||||
whoami
|
||||
ls -l /mnt/
|
||||
cat /mnt/DATALOSS_WARNING_README.txt
|
||||
sudo mkdir /mnt/x-aarch64
|
||||
sudo chown $(id -un):$(id -gn) /mnt/x-aarch64
|
||||
ls -l /mnt/
|
||||
|
||||
cleanup:
|
||||
if: ${{ inputs.cleanup }}
|
||||
needs: [inventory, peeky]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Cleaning up cruft ...
|
||||
run: |
|
||||
docker image prune -af
|
||||
docker volume prune -f
|
||||
docker container prune -f
|
||||
@@ -17,6 +17,7 @@ jobs:
|
||||
build:
|
||||
name: Regression Testing
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -31,18 +32,16 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: dl/
|
||||
key: dl-x86_64-netconf-${{ hashFiles('.git/modules/buildroot/refs/heads/master', 'package/*/*.hash') }}
|
||||
key: dl-netconf-${{ hashFiles('.git/modules/buildroot/refs/heads/master', 'package/*/*.hash') }}
|
||||
restore-keys: |
|
||||
dl-x86_64-netconf-
|
||||
dl-x86_64-
|
||||
dl-netconf-
|
||||
dl-
|
||||
- name: Restore Cache of .ccache/
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .ccache/
|
||||
key: ccache-x86_64-netconf-${{ hashFiles('.git/modules/buildroot/refs/head/master', 'package/*/*.hash') }}
|
||||
key: ccache-x86_64-${{ hashFiles('.git/modules/buildroot/refs/head/master', 'package/*/*.hash') }}
|
||||
restore-keys: |
|
||||
ccache-x86_64-netconf-
|
||||
ccache-x86_64-
|
||||
ccache-
|
||||
- name: Configure NETCONF
|
||||
|
||||
@@ -28,9 +28,14 @@ jobs:
|
||||
variant: [netconf, classic]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Maintenance
|
||||
run: |
|
||||
docker image prune -af
|
||||
docker volume prune -f
|
||||
docker container prune -f
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set Release Variables
|
||||
id: build
|
||||
id: vars
|
||||
run: |
|
||||
if [ -n "${{ inputs.version }}" ]; then
|
||||
ver=${{ inputs.version }}
|
||||
@@ -38,11 +43,6 @@ jobs:
|
||||
ver=${GITHUB_REF#refs/tags/}
|
||||
fi
|
||||
echo "ver=${ver}" >> $GITHUB_OUTPUT
|
||||
if echo $ver | grep -qE 'v[0-9\.]+(-alpha|-beta|-rc)[0-9]*'; then
|
||||
echo "pre=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "pre=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
fver=${ver#v}
|
||||
if [ "${{ matrix.variant }}" = "netconf" ]; then
|
||||
target=${{ matrix.platform }}-${fver}
|
||||
@@ -51,56 +51,86 @@ jobs:
|
||||
fi
|
||||
echo "dir=infix-$target" >> $GITHUB_OUTPUT
|
||||
echo "tgz=infix-$target.tar.gz" >> $GITHUB_OUTPUT
|
||||
echo "out=/mnt/x-$target" >> $GITHUB_OUTPUT
|
||||
- name: Restore Cache of dl/
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: dl/
|
||||
key: dl-${{ matrix.platform }}-${{ matrix.variant }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'configs/*', 'package/*/*.hash') }}
|
||||
key: dl-${{ matrix.variant }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'configs/*', 'package/*/*.hash') }}
|
||||
restore-keys: |
|
||||
dl-${{ matrix.platform }}-${{ matrix.variant }}-
|
||||
dl-${{ matrix.platform }}-
|
||||
dl-${{ matrix.variant }}-
|
||||
dl-
|
||||
- name: Restore Cache of .ccache/
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .ccache/
|
||||
key: ccache-${{ matrix.platform }}-${{ matrix.variant }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'package/*/*.hash') }}
|
||||
key: ccache-${{ matrix.platform }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'package/*/*.hash') }}
|
||||
restore-keys: |
|
||||
ccache-${{ matrix.platform }}-${{ matrix.variant }}-
|
||||
ccache-${{ matrix.platform }}-
|
||||
ccache-
|
||||
- name: Configure & Build
|
||||
env:
|
||||
INFIX_RELEASE: ${{ steps.build.outputs.ver }}
|
||||
INFIX_RELEASE: ${{ steps.vars.outputs.ver }}
|
||||
run: |
|
||||
if [ "${{ matrix.variant }}" = "netconf" ]; then
|
||||
target=${{ matrix.platform }}_defconfig
|
||||
else
|
||||
target=${{ matrix.platform }}_${{ matrix.variant }}_defconfig
|
||||
fi
|
||||
echo "Buildring $target ..."
|
||||
echo "Building $target ..."
|
||||
sudo mkdir ${{ steps.vars.outputs.out }}
|
||||
sudo chown $(id -un):$(id -gn) ${{ steps.vars.outputs.out }}
|
||||
export O=${{ steps.vars.outputs.out }}
|
||||
make $target
|
||||
make
|
||||
- name: Prepare Artifact
|
||||
run: |
|
||||
cd output
|
||||
mv images ${{ steps.build.outputs.dir }}
|
||||
ln -s ${{ steps.build.outputs.dir }} images
|
||||
tar chfz ${{ steps.build.outputs.tgz }} ${{ steps.build.outputs.dir }}
|
||||
cd ${{ steps.vars.outputs.out }}
|
||||
mv images ${{ steps.vars.outputs.dir }}
|
||||
ln -s ${{ steps.vars.outputs.dir }} images
|
||||
tar chfz ${{ steps.vars.outputs.tgz }} ${{ steps.vars.outputs.dir }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: output/${{ steps.build.outputs.tgz }}
|
||||
path: ${{ steps.vars.outputs.out }}/${{ steps.vars.outputs.tgz }}
|
||||
name: artifact-${{ matrix.variant }}-${{ matrix.platform }}
|
||||
release:
|
||||
name: Release Infix ${{ github.ref_name }}
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set Release Variables
|
||||
id: rel
|
||||
run: |
|
||||
if [ -n "${{ inputs.version }}" ]; then
|
||||
ver=${{ inputs.version }}
|
||||
else
|
||||
ver=${GITHUB_REF#refs/tags/}
|
||||
fi
|
||||
echo "ver=${ver}" >> $GITHUB_OUTPUT
|
||||
if echo $ver | grep -qE 'v[0-9.]+(-alpha|-beta|-rc)[0-9]*'; then
|
||||
echo "pre=true" >> $GITHUB_OUTPUT
|
||||
echo "latest=false" >> $GITHUB_OUTPUT
|
||||
elif echo $ver | grep -qE '^v[0-9.]+\.[0-9.]+(\.[0-9]+)?$'; then
|
||||
echo "pre=false" >> $GITHUB_OUTPUT
|
||||
echo "latest=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "pre=false" >> $GITHUB_OUTPUT
|
||||
echo "latest=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
echo "pre=${{ steps.rel.outputs.pre }}"
|
||||
echo "latest=${{ steps.rel.outputs.latest }}"
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: "artifact-*"
|
||||
merge-multiple: true
|
||||
- name: Create checksums ...
|
||||
run: |
|
||||
for file in *.tar.gz; do
|
||||
sha256sum $file > $file.sha256
|
||||
done
|
||||
- name: Extract ChangeLog entry ...
|
||||
run: |
|
||||
awk '/^-----*$/{if (x == 1) exit; x=1;next}x' doc/ChangeLog.md \
|
||||
@@ -109,9 +139,10 @@ jobs:
|
||||
- uses: ncipollo/release-action@v1
|
||||
with:
|
||||
name: Infix ${{ github.ref_name }}
|
||||
prerelease: ${{ needs.build.outputs.pre }}
|
||||
prerelease: ${{ steps.rel.outputs.pre }}
|
||||
makeLatest: ${{ steps.rel.outputs.latest }}
|
||||
bodyFile: release.md
|
||||
artifacts: artifact/*
|
||||
artifacts: "*.tar.gz*"
|
||||
- name: Summary
|
||||
run: |
|
||||
cat <<EOF >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
@@ -54,7 +54,7 @@ config INFIX_IMAGE_ID
|
||||
|
||||
config INFIX_TAGLINE
|
||||
string "Operating system tagline"
|
||||
default "Infix — a Network Operating System"
|
||||
default "Infix -- a Network Operating System"
|
||||
help
|
||||
Mandatory. Used for identifying the OS, e.g. as PRETTY_NAME in
|
||||
/etc/os-release and description in the GNS3 appliance.
|
||||
|
||||
@@ -22,12 +22,14 @@ $(config):
|
||||
@echo "'make <board>_defconfig' before building an image."
|
||||
@exit 1
|
||||
|
||||
|
||||
%: | buildroot/Makefile
|
||||
@+$(call bmake,$@)
|
||||
|
||||
# Workaround, see board/x86_64/board.mk
|
||||
test:
|
||||
@+$(call bmake,$@)
|
||||
|
||||
buildroot/Makefile:
|
||||
@git submodule update --init
|
||||
|
||||
.PHONY: all check
|
||||
|
||||
.PHONY: all check test
|
||||
|
||||
@@ -18,11 +18,39 @@
|
||||
|
||||
</details>
|
||||
|
||||
Infix is a Linux Network Operating System (NOS) based on [Buildroot][1],
|
||||
and [sysrepo][2]. A powerful mix that ease porting to different
|
||||
platforms, simplify long-term maintenance, and provide made-easy
|
||||
management using NETCONF[^1] (remote) or the built-in [command
|
||||
line interface (CLI)][3] (click the foldout for an example).
|
||||
Infix is a free, Linux based, immutable Network Operating System (NOS)
|
||||
built on [Buildroot][1], and [sysrepo][2]. A powerful mix that ease
|
||||
porting to different platforms, simplify long-term maintenance, and
|
||||
provide made-easy management using NETCONF[^1] or the built-in [command
|
||||
line interface (CLI)][3] from a console or SSH login. *Click the
|
||||
foldout (▶ Example CLI Session) below for an example.*
|
||||
|
||||
Although primarily focused on switches and routers, the core values
|
||||
may be appealing for other use-cases as well:
|
||||
|
||||
- Runs from a squashfs image on a read-only partition
|
||||
- Single configuration file on a separate partition
|
||||
- Built around YANG with standard IETF models
|
||||
- Linux switchdev provides open switch APIs
|
||||
- Atomic upgrades to secondary partition
|
||||
- Highly security focused
|
||||
|
||||
An immutable[^2] operating system enhances security and inherently makes
|
||||
it maintenance-free. Configuration and data, e.g, containers, is stored
|
||||
on separate partitions to ensure complete separation from system files
|
||||
and allow for seamless backup, restore, and provisioning.
|
||||
|
||||
In itself Infix is perfectly suited for dedicated networking tasks and
|
||||
native support for Docker containers provides a versatile platform that
|
||||
can easily be adapted to any customer need. Be it legacy applications,
|
||||
network protocols, process monitoring, or edge data analysis, it can run
|
||||
close to end equipment. Either directly connected on dedicated Ethernet
|
||||
ports or indirectly using virtual network cables to exist on the same
|
||||
LAN as other connected equipment.
|
||||
|
||||
The simple design of Infix provides complete control over both system
|
||||
and data, minimal cognitive burden, and makes it incredibly easy to get
|
||||
started.
|
||||
|
||||
<details><summary><b>Example CLI Session</b></summary>
|
||||
|
||||
@@ -78,6 +106,7 @@ The [following boards](board/aarch64/README.md) are fully supported:
|
||||
- Marvell CN9130 CRB
|
||||
- Marvell EspressoBIN
|
||||
- Microchip SparX-5i PCB135 (eMMC)
|
||||
- NanoPi R2S
|
||||
|
||||
An x86_64 build is also available, primarily intended for development
|
||||
and testing, but can also be used for evaluation and demo purposes. For
|
||||
@@ -92,6 +121,12 @@ more information, see: [Infix in Virtual Environments](doc/virtual.md).
|
||||
[^1]: NETCONF or RESTCONF, <https://datatracker.ietf.org/doc/html/rfc8040>,
|
||||
for more information, see [Infix Variants](doc/variant.md).
|
||||
|
||||
[^2]: An immutable operating system is one with read-only file systems,
|
||||
atomic updates, rollbacks, declarative configuration, and workload
|
||||
isolation. All to improve reliability, scalability, and security.
|
||||
For more information, see <https://ceur-ws.org/Vol-3386/paper9.pdf>
|
||||
and <https://www.zdnet.com/article/what-is-immutable-linux-heres-why-youd-run-an-immutable-linux-distro/>.
|
||||
|
||||
[1]: https://buildroot.org/
|
||||
[2]: https://www.sysrepo.org/
|
||||
[3]: doc/cli/introduction.md
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
infix {
|
||||
vpds = <&vpd_cpu &vpd_product &vpd_power>;
|
||||
usb-ports = <&cp0_usb3_1>;
|
||||
usb-port-names = "USB";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -268,7 +270,7 @@
|
||||
};
|
||||
|
||||
&cp0_usb3_1 {
|
||||
status = "disabled";
|
||||
status = "okay";
|
||||
usb-phy = <&cp0_usb3_0_phy1>;
|
||||
phys = <&cp0_utmi1>, <&cp0_comphy3 1>;
|
||||
phy-names = "usb", "usb3";
|
||||
|
||||
@@ -21,7 +21,6 @@ CONFIG_CFS_BANDWIDTH=y
|
||||
CONFIG_RT_GROUP_SCHED=y
|
||||
CONFIG_CGROUP_PIDS=y
|
||||
CONFIG_CGROUP_FREEZER=y
|
||||
CONFIG_CGROUP_HUGETLB=y
|
||||
CONFIG_CPUSETS=y
|
||||
CONFIG_CGROUP_DEVICE=y
|
||||
CONFIG_CGROUP_CPUACCT=y
|
||||
@@ -32,7 +31,6 @@ CONFIG_SCHED_AUTOGROUP=y
|
||||
CONFIG_BLK_DEV_INITRD=y
|
||||
CONFIG_KALLSYMS_ALL=y
|
||||
CONFIG_PROFILING=y
|
||||
CONFIG_SMP=y
|
||||
CONFIG_ARCH_SPARX5=y
|
||||
CONFIG_ARCH_MVEBU=y
|
||||
CONFIG_ARCH_VEXPRESS=y
|
||||
@@ -49,12 +47,6 @@ CONFIG_CPUFREQ_DT=y
|
||||
CONFIG_ARM_ARMADA_37XX_CPUFREQ=y
|
||||
CONFIG_ARM_ARMADA_8K_CPUFREQ=y
|
||||
CONFIG_ACPI=y
|
||||
CONFIG_ARM64_CRYPTO=y
|
||||
CONFIG_CRYPTO_SHA1_ARM64_CE=y
|
||||
CONFIG_CRYPTO_SHA2_ARM64_CE=y
|
||||
CONFIG_CRYPTO_GHASH_ARM64_CE=y
|
||||
CONFIG_CRYPTO_AES_ARM64_CE_CCM=y
|
||||
CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
|
||||
CONFIG_KPROBES=y
|
||||
CONFIG_JUMP_LABEL=y
|
||||
# CONFIG_GCC_PLUGINS is not set
|
||||
@@ -67,10 +59,8 @@ CONFIG_TRANSPARENT_HUGEPAGE=y
|
||||
CONFIG_CMA=y
|
||||
CONFIG_NET=y
|
||||
CONFIG_PACKET=y
|
||||
CONFIG_UNIX=y
|
||||
CONFIG_XDP_SOCKETS=y
|
||||
CONFIG_XDP_SOCKETS_DIAG=y
|
||||
CONFIG_INET=y
|
||||
CONFIG_IP_MULTICAST=y
|
||||
CONFIG_IP_ADVANCED_ROUTER=y
|
||||
CONFIG_IP_MULTIPLE_TABLES=y
|
||||
@@ -112,7 +102,6 @@ CONFIG_NFT_MASQ=m
|
||||
CONFIG_NFT_REDIR=m
|
||||
CONFIG_NFT_NAT=m
|
||||
CONFIG_NFT_TUNNEL=m
|
||||
CONFIG_NFT_OBJREF=m
|
||||
CONFIG_NFT_QUEUE=m
|
||||
CONFIG_NFT_REJECT=m
|
||||
CONFIG_NFT_COMPAT=m
|
||||
@@ -186,9 +175,7 @@ CONFIG_VLAN_8021Q=y
|
||||
CONFIG_VLAN_8021Q_GVRP=y
|
||||
CONFIG_VLAN_8021Q_MVRP=y
|
||||
CONFIG_NET_SCHED=y
|
||||
CONFIG_NET_SCH_CBQ=y
|
||||
CONFIG_NET_CLS_BASIC=y
|
||||
CONFIG_NET_CLS_TCINDEX=y
|
||||
CONFIG_NET_CLS_BPF=y
|
||||
CONFIG_NET_CLS_FLOWER=y
|
||||
CONFIG_NETLINK_DIAG=y
|
||||
@@ -236,7 +223,6 @@ CONFIG_ATA=y
|
||||
CONFIG_SATA_AHCI=y
|
||||
CONFIG_SATA_AHCI_PLATFORM=y
|
||||
CONFIG_AHCI_MVEBU=y
|
||||
CONFIG_PATA_PLATFORM=y
|
||||
CONFIG_PATA_OF_PLATFORM=y
|
||||
CONFIG_MD=y
|
||||
CONFIG_BLK_DEV_DM=y
|
||||
@@ -325,6 +311,7 @@ CONFIG_8139CP=y
|
||||
# CONFIG_NET_VENDOR_WIZNET is not set
|
||||
# CONFIG_NET_VENDOR_XILINX is not set
|
||||
CONFIG_SFP=y
|
||||
CONFIG_AX88796B_PHY=y
|
||||
CONFIG_MARVELL_PHY=y
|
||||
CONFIG_MARVELL_10G_PHY=m
|
||||
CONFIG_MICREL_PHY=y
|
||||
@@ -332,12 +319,12 @@ CONFIG_MICROCHIP_PHY=y
|
||||
CONFIG_MICROCHIP_T1_PHY=y
|
||||
CONFIG_MICROSEMI_PHY=m
|
||||
CONFIG_MDIO_BITBANG=y
|
||||
CONFIG_MDIO_MVUSB=y
|
||||
CONFIG_MDIO_MVUSB=m
|
||||
CONFIG_MDIO_MSCC_MIIM=y
|
||||
CONFIG_MDIO_BUS_MUX_MMIOREG=y
|
||||
CONFIG_USB_RTL8152=y
|
||||
CONFIG_USB_USBNET=y
|
||||
CONFIG_USB_NET_CDC_EEM=y
|
||||
CONFIG_USB_RTL8152=m
|
||||
CONFIG_USB_USBNET=m
|
||||
CONFIG_USB_NET_CDC_EEM=m
|
||||
# CONFIG_USB_NET_CDC_NCM is not set
|
||||
# CONFIG_USB_NET_NET1080 is not set
|
||||
# CONFIG_USB_NET_CDC_SUBSET is not set
|
||||
@@ -371,7 +358,6 @@ CONFIG_I2C_MV64XXX=y
|
||||
CONFIG_I2C_SLAVE=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_SPI_ARMADA_3700=y
|
||||
CONFIG_SPI_NXP_FLEXSPI=y
|
||||
CONFIG_SPI_ORION=y
|
||||
CONFIG_SPI_PL022=y
|
||||
CONFIG_SPI_MUX=y
|
||||
@@ -427,30 +413,31 @@ CONFIG_REGULATOR_S2MPS11=y
|
||||
# CONFIG_HID_LOGITECH is not set
|
||||
# CONFIG_HID_MICROSOFT is not set
|
||||
# CONFIG_HID_MONTEREY is not set
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_ULPI_BUS=y
|
||||
CONFIG_USB=m
|
||||
CONFIG_USB_OTG=y
|
||||
CONFIG_USB_XHCI_HCD=y
|
||||
CONFIG_USB_XHCI_MVEBU=y
|
||||
CONFIG_USB_EHCI_HCD=y
|
||||
CONFIG_USB_EHCI_HCD_PLATFORM=y
|
||||
CONFIG_USB_OHCI_HCD=y
|
||||
CONFIG_USB_OHCI_HCD_PLATFORM=y
|
||||
CONFIG_USB_STORAGE=y
|
||||
CONFIG_USB_XHCI_HCD=m
|
||||
CONFIG_USB_XHCI_MVEBU=m
|
||||
CONFIG_USB_EHCI_HCD=m
|
||||
CONFIG_USB_EHCI_HCD_PLATFORM=m
|
||||
CONFIG_USB_OHCI_HCD=m
|
||||
CONFIG_USB_OHCI_HCD_PLATFORM=m
|
||||
CONFIG_USB_STORAGE=m
|
||||
CONFIG_USB_DWC3=y
|
||||
CONFIG_USB_DWC2=y
|
||||
CONFIG_USB_CHIPIDEA=y
|
||||
CONFIG_USB_CHIPIDEA=m
|
||||
CONFIG_USB_CHIPIDEA_UDC=y
|
||||
CONFIG_USB_CHIPIDEA_HOST=y
|
||||
CONFIG_USB_ISP1760=y
|
||||
CONFIG_USB_HSIC_USB3503=y
|
||||
CONFIG_USB_HSIC_USB3503=m
|
||||
CONFIG_NOP_USB_XCEIV=y
|
||||
CONFIG_USB_ULPI=y
|
||||
CONFIG_USB_GADGET=y
|
||||
CONFIG_USB_SNP_UDC_PLAT=y
|
||||
CONFIG_TYPEC=y
|
||||
CONFIG_TYPEC_TCPM=y
|
||||
CONFIG_TYPEC_TCPCI=y
|
||||
CONFIG_TYPEC_RT1711H=y
|
||||
CONFIG_TYPEC_TCPM=m
|
||||
CONFIG_TYPEC_TCPCI=m
|
||||
CONFIG_TYPEC_RT1711H=m
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_BLOCK_MINORS=32
|
||||
CONFIG_MMC_ARMMMCI=y
|
||||
@@ -462,7 +449,6 @@ CONFIG_MMC_SPI=y
|
||||
CONFIG_MMC_DW=y
|
||||
CONFIG_MMC_DW_EXYNOS=y
|
||||
CONFIG_MMC_DW_K3=y
|
||||
CONFIG_MMC_CQHCI=y
|
||||
CONFIG_MMC_SDHCI_XENON=y
|
||||
CONFIG_SCSI_UFSHCD=y
|
||||
CONFIG_SCSI_UFSHCD_PLATFORM=y
|
||||
@@ -503,7 +489,6 @@ CONFIG_EXTCON_USB_GPIO=y
|
||||
CONFIG_IIO=y
|
||||
CONFIG_TI_ADC081C=y
|
||||
CONFIG_PWM=y
|
||||
CONFIG_PHY_XGENE=y
|
||||
CONFIG_PHY_MVEBU_CP110_COMPHY=y
|
||||
CONFIG_PHY_MVEBU_CP110_UTMI=y
|
||||
CONFIG_PHY_SAMSUNG_USB2=y
|
||||
@@ -512,13 +497,11 @@ CONFIG_MUX_MMIO=y
|
||||
CONFIG_EXT2_FS=y
|
||||
CONFIG_EXT3_FS=y
|
||||
CONFIG_EXT4_FS_POSIX_ACL=y
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_BTRFS_FS=y
|
||||
CONFIG_BTRFS_FS_POSIX_ACL=y
|
||||
CONFIG_FANOTIFY=y
|
||||
CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y
|
||||
CONFIG_QUOTA=y
|
||||
CONFIG_AUTOFS4_FS=y
|
||||
CONFIG_FUSE_FS=y
|
||||
CONFIG_VIRTIO_FS=y
|
||||
CONFIG_OVERLAY_FS=y
|
||||
@@ -536,17 +519,21 @@ CONFIG_SECURITY=y
|
||||
CONFIG_CRYPTO_CCM=m
|
||||
CONFIG_CRYPTO_ECHAINIV=y
|
||||
CONFIG_CRYPTO_ANSI_CPRNG=y
|
||||
CONFIG_CRYPTO_GHASH_ARM64_CE=y
|
||||
CONFIG_CRYPTO_SHA1_ARM64_CE=y
|
||||
CONFIG_CRYPTO_SHA2_ARM64_CE=y
|
||||
CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
|
||||
CONFIG_CRYPTO_AES_ARM64_CE_CCM=y
|
||||
CONFIG_DMA_CMA=y
|
||||
CONFIG_CMA_SIZE_MBYTES=0
|
||||
CONFIG_IRQ_POLL=y
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_DEBUG_KERNEL=y
|
||||
CONFIG_MAGIC_SYSRQ=y
|
||||
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
|
||||
CONFIG_MAGIC_SYSRQ=y
|
||||
CONFIG_DEBUG_FS=y
|
||||
CONFIG_DETECT_HUNG_TASK=y
|
||||
# CONFIG_SCHED_DEBUG is not set
|
||||
# CONFIG_DEBUG_PREEMPT is not set
|
||||
# CONFIG_RCU_TRACE is not set
|
||||
CONFIG_FUNCTION_TRACER=y
|
||||
# CONFIG_STRICT_DEVMEM is not set
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# Automatically generated make config: don't edit
|
||||
# Busybox version: 1.36.0
|
||||
# Tue Oct 3 18:00:40 2023
|
||||
# Thu Feb 8 08:22:32 2024
|
||||
#
|
||||
CONFIG_HAVE_DOT_CONFIG=y
|
||||
|
||||
@@ -1132,9 +1132,9 @@ CONFIG_SV_DEFAULT_SERVICE_DIR=""
|
||||
CONFIG_SH_IS_ASH=y
|
||||
# CONFIG_SH_IS_HUSH is not set
|
||||
# CONFIG_SH_IS_NONE is not set
|
||||
CONFIG_BASH_IS_ASH=y
|
||||
# CONFIG_BASH_IS_ASH is not set
|
||||
# CONFIG_BASH_IS_HUSH is not set
|
||||
# CONFIG_BASH_IS_NONE is not set
|
||||
CONFIG_BASH_IS_NONE=y
|
||||
CONFIG_SHELL_ASH=y
|
||||
CONFIG_ASH=y
|
||||
CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
|
||||
|
||||
@@ -22,7 +22,7 @@ diskimg=disk.img
|
||||
ver()
|
||||
{
|
||||
if [ -n "$INFIX_RELEASE" ]; then
|
||||
printf -- "-%s" "$INFIX_RELEASE"
|
||||
printf -- "-%s" "${INFIX_RELEASE#v}"
|
||||
return
|
||||
fi
|
||||
}
|
||||
@@ -42,7 +42,7 @@ fi
|
||||
load_cfg DISK_IMAGE
|
||||
if [ "$DISK_IMAGE" = "y" ]; then
|
||||
ixmsg "Creating Disk Image"
|
||||
diskimg="${NAME}-disk.img"
|
||||
diskimg="${NAME}-disk$(ver).img"
|
||||
bootcfg=
|
||||
if [ "$DISK_IMAGE_BOOT_DATA" ]; then
|
||||
bootcfg="-b $DISK_IMAGE_BOOT_DATA -B $DISK_IMAGE_BOOT_OFFSET"
|
||||
|
||||
@@ -75,9 +75,9 @@ config QEMU_MACHINE
|
||||
|
||||
config QEMU_MACHINE_RAM
|
||||
string "RAM size (k/M/G)"
|
||||
default "256M"
|
||||
default "384M"
|
||||
help
|
||||
The default, 255 MiB, works for most configurations, even less for
|
||||
The default, 384 MiB, works for most configurations, even less for
|
||||
the Infix Classic builds. However, if you get kernel panic with:
|
||||
"System is deadlocked on memory", try increasing this one.
|
||||
|
||||
|
||||
@@ -24,11 +24,22 @@ prognm=$(basename "$0")
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "usage: $prognm [opts]"
|
||||
echo "Usage:"
|
||||
echo " $prognm [opts] [ARGS]"
|
||||
echo
|
||||
echo " -c Run menuconfig to change Qemu settings"
|
||||
echo " -h This help text"
|
||||
echo "Options:"
|
||||
echo " -c Run menuconfig to change Qemu settings"
|
||||
echo " -h This help text"
|
||||
echo
|
||||
echo "Arguments:"
|
||||
echo " ARGS1 Args before the '--' separator are for kernel space"
|
||||
echo " -- Separator"
|
||||
echo " ARGS2 Args after the '--' separator are for the init process"
|
||||
echo " Also, qemu.cfg has QEMU_APPEND which can affect this."
|
||||
echo
|
||||
echo "Example:"
|
||||
echo " qemu.sh -- finit.debug"
|
||||
echo "___________________________________________________________________"
|
||||
echo "Note: 'kconfig-frontends' package (Debian/Ubuntu) must be installed"
|
||||
echo " for -c to work: sudo apt install kconfig-frontents"
|
||||
|
||||
@@ -130,6 +141,18 @@ serial_args()
|
||||
echo -n "-device virtconsole,nr=1,name=gdbserver,chardev=gdbserver "
|
||||
}
|
||||
|
||||
usb_args()
|
||||
{
|
||||
USBSTICK="usb.vfat"
|
||||
if ! [ -f $USBSTICK ]; then
|
||||
dd if=/dev/zero of=${USBSTICK} bs=8M count=1 >/dev/null 2>&1
|
||||
mkfs.vfat $USBSTICK >/dev/null 2>&1
|
||||
fi
|
||||
echo -n "-drive if=none,id=usbstick,format=raw,file=${USBSTICK} "
|
||||
echo -n "-usb "
|
||||
echo -n "-device usb-ehci,id=ehci "
|
||||
echo -n "-device usb-storage,bus=ehci.0,drive=usbstick "
|
||||
}
|
||||
rw_args()
|
||||
{
|
||||
[ "$CONFIG_QEMU_RW" ] || return
|
||||
@@ -235,6 +258,7 @@ run_qemu()
|
||||
$(rootfs_args) \
|
||||
$(serial_args) \
|
||||
$(rw_args) \
|
||||
$(usb_args) \
|
||||
$(host_args) \
|
||||
$(net_args) \
|
||||
$(wdt_args) \
|
||||
@@ -346,6 +370,10 @@ else
|
||||
load_qemucfg qemu.cfg
|
||||
fi
|
||||
|
||||
if [ -z "$QEMU_EXTRA_APPEND" ]; then
|
||||
QEMU_EXTRA_APPEND="$*"
|
||||
fi
|
||||
|
||||
generate_dot
|
||||
|
||||
echo "Starting Qemu :: Ctrl-a x -- exit | Ctrl-a c -- toggle console/monitor"
|
||||
@@ -353,4 +381,3 @@ line=$(stty -g)
|
||||
stty raw
|
||||
run_qemu $(dtb_args)
|
||||
stty "$line"
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/usr/bin/mg
|
||||
@@ -0,0 +1,4 @@
|
||||
service :%i pid:!/run/k8s-logger-%i.pid <usr/container:%i> \
|
||||
[2345] k8s-logger -cni %i -f local1 /run/containers/%i.fifo -- Logger for container %i
|
||||
sysv :%i pid:!/run/container:%i.pid <pid/k8s-logger:%i> kill:10 \
|
||||
[2345] container -n %i -- container %i
|
||||
@@ -0,0 +1,3 @@
|
||||
# Do not authorize usb ports before they have been checked in in the device-tree
|
||||
options usbcore authorized_default=0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.-------.
|
||||
| . . | Infix -- a Network Operating System
|
||||
|-. v .-| https://kernelkit.github.io
|
||||
'-'---'-'
|
||||
'-'---'-'
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
local1.* -/var/log/container
|
||||
@@ -1 +1 @@
|
||||
mg
|
||||
/etc/alternatives/editor
|
||||
@@ -1 +1 @@
|
||||
mg
|
||||
/etc/alternatives/editor
|
||||
Executable
+590
@@ -0,0 +1,590 @@
|
||||
#!/bin/sh
|
||||
|
||||
all=""
|
||||
env=""
|
||||
port=""
|
||||
force=
|
||||
|
||||
log()
|
||||
{
|
||||
logger -I $PPID -t container -p local1.notice -- "$*"
|
||||
}
|
||||
|
||||
# Unpacks a given oci-archive.tar[.gz] in the current directory. Sanity
|
||||
# checks, at least one index.json in the top-level dir of the archive.
|
||||
# If there are more index files, this function does not handle them.
|
||||
unpack_archive()
|
||||
{
|
||||
image=$1
|
||||
name=$2
|
||||
|
||||
# Supported transports for load and create
|
||||
case "$image" in
|
||||
oci:*) # Unpacked OCI image
|
||||
file=${image#oci:}
|
||||
;;
|
||||
oci-archive:*) # Packed OCI image, .tar or .tar.gz format
|
||||
file=${image#oci-archive:}
|
||||
;;
|
||||
*) # docker://*, docker-archive:*, or URL
|
||||
echo "$image"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ ! -e "$file" ]; then
|
||||
if [ -e "/var/lib/conatainers/oci/$file" ]; then
|
||||
file="/var/lib/conatainers/oci/$file"
|
||||
elif [ -e "/lib/oci/$file" ]; then
|
||||
file="/lib/oci/$file"
|
||||
else
|
||||
log "Error: cannot find OCI archive $file in search path."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -d "$file" ]; then
|
||||
index=$(find "$file" -name index.json)
|
||||
if [ -z "$index" ]; then
|
||||
log "Error: cannot find index.json in OCI image $file"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
index=$(tar tf "$file" |grep index.json)
|
||||
if [ -z "$index" ]; then
|
||||
log "Error: invalid OCI archive, cannot find index.json in $file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[ -n "$quiet" ] || log "Extracting OCI archive $file ..."
|
||||
tar xf "$file" || (log "Error: failed unpacking $file in $(pwd)"; exit 1)
|
||||
fi
|
||||
|
||||
dir=$(dirname "$index")
|
||||
[ -n "$quiet" ] || log "Loading OCI image $dir ..."
|
||||
podman load -qi "$dir" >/dev/null
|
||||
|
||||
# Rename image from podman default $dir:latest
|
||||
if [ -n "$name" ]; then
|
||||
podman tag "$dir" "$name" >/dev/null
|
||||
podman rmi "$dir" >/dev/null
|
||||
else
|
||||
name=$dir
|
||||
fi
|
||||
|
||||
echo "$name"
|
||||
}
|
||||
|
||||
running()
|
||||
{
|
||||
run=$(podman inspect "$1" 2>/dev/null |jq .[].State.Running)
|
||||
[ "$run" = "true" ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
create()
|
||||
{
|
||||
name=$1
|
||||
image=$2
|
||||
shift 2
|
||||
|
||||
if [ -z "$name" ] || [ -z "$image" ]; then
|
||||
echo "Usage:"
|
||||
echo " container create NAME IMAGE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Unpack and load docker-archive/oci/oci-archive, returning image
|
||||
# name, or return docker:// URL for download.
|
||||
image=$(unpack_archive "$image")
|
||||
|
||||
if [ -z "$logging" ]; then
|
||||
logging="--log-driver k8s-file --log-opt path=/run/containers/$name.fifo"
|
||||
fi
|
||||
|
||||
args="$args --cgroup-parent=containers"
|
||||
args="$args --restart=$restart --systemd=false --tz=local $privileged --replace --quiet"
|
||||
args="$args $ro $vol $mount $hostname $entrypoint $env $port $logging"
|
||||
pidfn=/run/container:${name}.pid
|
||||
|
||||
[ -n "$quiet" ] || log "---------------------------------------"
|
||||
[ -n "$quiet" ] || log "Got name: $name image: $image"
|
||||
[ -n "$quiet" ] || log "Got networks: $network"
|
||||
|
||||
if [ -n "$network" ]; then
|
||||
for net in $network; do
|
||||
args="$args --net=$net"
|
||||
done
|
||||
|
||||
for srv in $dns; do
|
||||
args="$args --dns=$srv"
|
||||
done
|
||||
|
||||
for domain in $search; do
|
||||
args="$args --dns-search=$domain"
|
||||
done
|
||||
else
|
||||
network="--net none"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2048
|
||||
if podman create --name "$name" --conmon-pidfile="$pidfn" $args "$image" $*; then
|
||||
[ -n "$quiet" ] || log "Successfully created container $name from $image"
|
||||
rm -f "/run/containers/env/${name}.env"
|
||||
[ -n "$manual" ] || start "$name"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Error: failed creating container $name, please check the configuration."
|
||||
exit 1
|
||||
}
|
||||
|
||||
delete()
|
||||
{
|
||||
name=$1
|
||||
image=$2
|
||||
|
||||
if [ -z "$name" ]; then
|
||||
echo "Usage:"
|
||||
echo " container delete NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
podman rm -vif "$name" >/dev/null 2>&1
|
||||
[ -n "$quiet" ] || log "Container $name has been removed."
|
||||
}
|
||||
|
||||
waitfor()
|
||||
{
|
||||
timeout=$2
|
||||
while [ ! -f "$1" ]; do
|
||||
_=$((timeout -= 1))
|
||||
if [ $timeout -le 0 ]; then
|
||||
log "Timeout waiting for $1, aborting!"
|
||||
exit 1
|
||||
fi
|
||||
sleep 1;
|
||||
done
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
name=$1
|
||||
|
||||
if running "$name"; then
|
||||
[ -n "$quiet" ] || echo "$name: already running."
|
||||
return
|
||||
fi
|
||||
|
||||
initctl -bq cond set "container:$name"
|
||||
# Real work is done by wrap() courtesy of finit sysv emulation
|
||||
}
|
||||
|
||||
stop()
|
||||
{
|
||||
name=$1
|
||||
|
||||
if ! running "$name"; then
|
||||
[ -n "$quiet" ] || echo "$name: not running."
|
||||
return
|
||||
fi
|
||||
|
||||
initctl -bq cond clr "container:$name"
|
||||
# Real work is done by wrap() courtesy of finit sysv emulation
|
||||
}
|
||||
|
||||
wrap()
|
||||
{
|
||||
name=$1
|
||||
cmd=$2
|
||||
|
||||
podman "$cmd" "$name"
|
||||
}
|
||||
|
||||
# Removes network $1 from all containers
|
||||
netwrm()
|
||||
{
|
||||
net=$1
|
||||
|
||||
for c in $(podman ps $all --format "{{.Names}}"); do
|
||||
for n in $(podman inspect "$c" |jq -r '.[].NetworkSettings.Networks | keys[]'); do
|
||||
if [ "$n" = "$net" ]; then
|
||||
podman network disconnect $force "$n" "$c" >/dev/null
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
usage()
|
||||
{
|
||||
cat <<EOF
|
||||
usage:
|
||||
container [opt] cmd [arg]
|
||||
|
||||
options:
|
||||
-a, --all Show all, of something
|
||||
--dns NAMESERVER Set nameserver(s) when creating a container
|
||||
--dns-search LIST Set host lookup search list when creating container
|
||||
-c, --creds USR[:PWD] Credentials to pass to curl -u for remote ops
|
||||
-d, --detach Detach a container started with 'run IMG [CMD]'
|
||||
-e, --env FILE Environment variables when creating container
|
||||
--entrypoint Disable container image's ENTRYPOINT, run cmd + arg
|
||||
-f, --force Force operation, e.g. remove
|
||||
-h, --help Show this help text
|
||||
--hostname NAME Set hostname when creating container
|
||||
--net NETWORK Network interface(s) when creating or finding container
|
||||
-l, --log-driver DRV Log driver to use
|
||||
--log-opt OPT Logging options to log driver
|
||||
--log-path PATH Path for k8s-file log pipe
|
||||
-m, --mount HOST:DEST Bind mount a read-only file inside a container
|
||||
--manual Do not start container automatically after creation
|
||||
-n, --name NAME Alternative way of supplying name to start/stop/restart
|
||||
--privileged Give container extended privileges
|
||||
-p, --publish PORT Publish ports when creating container
|
||||
Syntax: [[ip:][hostPort]:]containerPort[/protocol]
|
||||
-q, --quiet Quiet operation, called from confd
|
||||
-r, --restart POLICY One of "no", "always", or "on-failure:NUM"
|
||||
--read-only Do not create a writable layer
|
||||
-s, --simple Show output in simplified format
|
||||
-v, --volume NAME:PATH Create named volume mounted inside container on PATH
|
||||
|
||||
commands:
|
||||
create NAME IMAGE NET Create container NAME using IMAGE with networks NET
|
||||
delete [network] NAME Remove container NAME or network NAME from all containers
|
||||
exec NAME CMD Run a command inside a container
|
||||
find [ifname PID] Find PID of container where '--net IFNAME' currently lives
|
||||
or, find the name of our IFNAME inside the container @PID
|
||||
help Show this help text
|
||||
list [image | oci] List names (only) of containers, images, or OCI archives
|
||||
load [NAME | URL] NM Load OCI tarball fileNAME or URL to image NM
|
||||
remove IMAGE Remove an (unused) container image
|
||||
restart [NAME] Restart a crashed container
|
||||
run NAME [CMD] Run a container interactively, with an optional command
|
||||
save IMAGE FILE Save a container image to an OCI tarball FILE[.tar.gz]
|
||||
shell Start a shell inside a container
|
||||
show [image | volume] Show containers, images, or volumes
|
||||
stat Show continuous stats about containers (Ctrl-C aborts)
|
||||
start [NAME] Start a container, see -n
|
||||
stop [NAME] Stop a container, see -n
|
||||
volume [prune] Prune unused volumes
|
||||
EOF
|
||||
}
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
case $1 in
|
||||
-a | --all)
|
||||
all="-a"
|
||||
;;
|
||||
-c | --creds)
|
||||
shift
|
||||
creds="-u $1"
|
||||
;;
|
||||
-d | --detach)
|
||||
detach="-d"
|
||||
;;
|
||||
--dns)
|
||||
shift
|
||||
dns="$dns $1"
|
||||
;;
|
||||
--dns-search)
|
||||
shift
|
||||
search="$search $1"
|
||||
;;
|
||||
-e | --env)
|
||||
shift
|
||||
env="$env --env-file=$1"
|
||||
;;
|
||||
--entrypoint)
|
||||
entrypoint="--entrypoint=\"\""
|
||||
;;
|
||||
-f | --force)
|
||||
force="-f"
|
||||
;;
|
||||
-h | --help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--hostname)
|
||||
shift
|
||||
hostname="--hostname $1"
|
||||
;;
|
||||
-l | --log-driver)
|
||||
shift
|
||||
logging=" --log-driver=$1"
|
||||
;;
|
||||
--log-opt)
|
||||
shift
|
||||
logging="$logging --log-opt $1"
|
||||
;;
|
||||
--log-path)
|
||||
shift
|
||||
logging="$logging --log-opt path=$1"
|
||||
log_path="$1"
|
||||
;;
|
||||
-m | --mount)
|
||||
shift
|
||||
mount="--mount=$1"
|
||||
;;
|
||||
--manual)
|
||||
manual=true
|
||||
;;
|
||||
-n | --name)
|
||||
shift
|
||||
name="$1"
|
||||
;;
|
||||
--net)
|
||||
shift
|
||||
if [ -n "$network" ]; then
|
||||
network="$network $1"
|
||||
else
|
||||
network=$1
|
||||
fi
|
||||
;;
|
||||
--privileged)
|
||||
privileged="--privileged=true"
|
||||
;;
|
||||
-p | --publish)
|
||||
shift
|
||||
port="$port -p $1"
|
||||
;;
|
||||
-q | --quiet)
|
||||
quiet="-q"
|
||||
;;
|
||||
-r | --restart)
|
||||
shift
|
||||
restart=$1
|
||||
;;
|
||||
--read-only)
|
||||
ro="--read-only=true"
|
||||
;;
|
||||
-s | --simple)
|
||||
simple=true
|
||||
;;
|
||||
-v | --volume)
|
||||
shift
|
||||
vol="$vol -v $1"
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
cmd=$1
|
||||
if [ -n "$cmd" ]; then
|
||||
shift
|
||||
fi
|
||||
|
||||
case $cmd in
|
||||
# Does not work atm., cannot attach to TTY because
|
||||
# we monitor 'podman start -ai foo' with Finit.
|
||||
# attach)
|
||||
# podman attach "$1"
|
||||
# ;;
|
||||
create)
|
||||
[ -n "$quiet" ] || log "Got create args: $*"
|
||||
create "$@"
|
||||
;;
|
||||
delete)
|
||||
cmd=$1
|
||||
name=$2
|
||||
if [ "$cmd" = "network" ] && [ -n "$name" ]; then
|
||||
netwrm "$name"
|
||||
else
|
||||
delete "$@"
|
||||
fi
|
||||
;;
|
||||
exec)
|
||||
podman exec -it "$@"
|
||||
;;
|
||||
find)
|
||||
cmd=$1
|
||||
pid=$2
|
||||
if [ "$cmd" = "ifname" ] && [ -n "$pid" ]; then
|
||||
nsenter -t "$pid" -n ip -d -j link | \
|
||||
jq --arg ifname "$network" -r '.[] | select(.ifalias==$ifname) | .ifname'
|
||||
else
|
||||
containers=$(podman ps $all --format "{{.Names}}")
|
||||
for c in $containers; do
|
||||
json=$(podman inspect "$c")
|
||||
nets=$(echo "$json" |jq -r '.[].NetworkSettings.Networks | keys[]' 2>/dev/null)
|
||||
for n in $nets; do
|
||||
if [ "$network" = "$n" ]; then
|
||||
pid=$(echo "$json" | jq .[].State.Pid)
|
||||
echo "$pid"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
done
|
||||
fi
|
||||
;;
|
||||
help)
|
||||
usage
|
||||
;;
|
||||
load)
|
||||
url=$1
|
||||
name=$2
|
||||
# shellcheck disable=SC2086
|
||||
if echo "$url" | grep -q "://"; then
|
||||
file=$(basename "$url")
|
||||
curl -k $creds -Lo "$file" "$url"
|
||||
else
|
||||
file="$url"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
name=$(unpack_archive "$file" $name)
|
||||
|
||||
# Show resulting image(s) matching $name
|
||||
if [ -n "$name" ]; then
|
||||
podman images -n "$name"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
ls | list)
|
||||
cmd=$1
|
||||
[ -n "$cmd" ] && shift
|
||||
case $cmd in
|
||||
image*)
|
||||
podman images $all --format "{{.Repository}}:{{.Tag}}"
|
||||
;;
|
||||
oci)
|
||||
find /lib/oci /var/lib/containers/oci -type f 2>/dev/null
|
||||
;;
|
||||
*)
|
||||
podman ps $all --format "{{.Names}}"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
pull)
|
||||
podman pull "$@"
|
||||
;;
|
||||
remove)
|
||||
podman rmi $all $force -i "$1"
|
||||
;;
|
||||
run)
|
||||
img=$1
|
||||
cmd=$2
|
||||
[ -n "$port" ] || port="-P"
|
||||
if [ -n "$cmd" ]; then
|
||||
shift 2
|
||||
[ -n "$detach" ] || echo "Starting $img ENTRYPOINT $cmd :: use Ctrl-p Ctrl-q to detach"
|
||||
podman run -it --rm $detach $port --entrypoint="$cmd" "$img" "$@"
|
||||
else
|
||||
[ -n "$detach" ] || echo "Starting $img :: use Ctrl-p Ctrl-q to detach"
|
||||
podman run -it --rm $detach $port "$img"
|
||||
fi
|
||||
;;
|
||||
save)
|
||||
name=$1
|
||||
file=$2
|
||||
if echo "$file" | grep -q ".gz"; then
|
||||
file=${file%%.gz}
|
||||
gzip=true
|
||||
fi
|
||||
if ! echo "$file" | grep -q ".tar"; then
|
||||
file=${file}.tar
|
||||
gzip=true
|
||||
fi
|
||||
podman save -o "$file" "$name"
|
||||
if [ -s "$file" ] && [ -n "$gzip" ]; then
|
||||
gzip "$file"
|
||||
fi
|
||||
;;
|
||||
shell)
|
||||
podman exec -it "$1" sh -l
|
||||
;;
|
||||
show)
|
||||
cmd=$1
|
||||
[ -n "$cmd" ] && shift
|
||||
case $cmd in
|
||||
image*)
|
||||
if [ -n "$simple" ]; then
|
||||
podman images $all --format "{{.Names}} {{.Size}}" \
|
||||
| sed 's/\[\(.*\)\] /\1 /g' \
|
||||
| awk '{ printf "%-60s %s %s\n", $1, $2, $3}'
|
||||
else
|
||||
podman images $all
|
||||
fi
|
||||
;;
|
||||
volume*)
|
||||
printf "%-20s CONTAINER\n" "VOLUME"
|
||||
for v in $(podman volume ls --format "{{.Name}}"); do
|
||||
printf "%-20s" "$v"
|
||||
podman ps -a --filter volume="$v" --format '{{.Names}}' | sed 's/^/ /'
|
||||
done
|
||||
;;
|
||||
*)
|
||||
if [ -n "$simple" ]; then
|
||||
podman ps $all --format "{{.ID}} {{.Names}} {{.Image}}" \
|
||||
| awk '{ printf "%s %-30s %s\n", $1, $2, $3}'
|
||||
else
|
||||
podman ps $all
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
start)
|
||||
if [ -n "$name" ]; then
|
||||
wrap "$name" start
|
||||
elif [ -n "$1" ]; then
|
||||
start "$1"
|
||||
else
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
restart)
|
||||
if [ -n "$name" ]; then
|
||||
wrap "$name" restart
|
||||
elif [ -n "$1" ]; then
|
||||
stop "$1"
|
||||
start "$1"
|
||||
else
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
stop)
|
||||
if [ -n "$name" ]; then
|
||||
wrap "$name" stop
|
||||
elif [ -n "$1" ]; then
|
||||
stop "$1"
|
||||
else
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
stat*)
|
||||
podman stats -i 2
|
||||
;;
|
||||
upgrade)
|
||||
img=$(podman inspect "$1" | jq -r .[].ImageName)
|
||||
if [ -z "$img" ]; then
|
||||
echo "No such container ($1), or invalid ImageName. Cannot upgrade."
|
||||
exit 1;
|
||||
fi
|
||||
podman stop "$1"
|
||||
podman pull "$img" || (echo "Failed fetching $img, check your network (settings)."; exit 1)
|
||||
"/var/lib/containers/active/${1}.sh" || (echo "Failed recreating container $1"; exit 1)
|
||||
;;
|
||||
volume)
|
||||
cmd=$1
|
||||
[ -n "$cmd" ] && shift
|
||||
case $cmd in
|
||||
prune)
|
||||
podman volume $force prune
|
||||
;;
|
||||
*)
|
||||
false
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC3045
|
||||
|
||||
OUTPUT=$1
|
||||
|
||||
read -r -s -p "New password: " password
|
||||
>&2 echo
|
||||
read -r -s -p "Retype password: " password_again
|
||||
>&2 echo
|
||||
|
||||
if [ "$password" != "$password_again" ]; then
|
||||
echo "Passwords do not match, try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$OUTPUT" ]; then
|
||||
echo "$password"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
umask 0177
|
||||
echo "$password" | mkpasswd > "$OUTPUT"
|
||||
exit 0
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
|
||||
def get_ethtool_output(interface):
|
||||
try:
|
||||
output = subprocess.check_output(['ethtool', interface], stderr=subprocess.DEVNULL, text=True)
|
||||
return output.splitlines()
|
||||
except subprocess.CalledProcessError:
|
||||
print("Error: Failed to run ethtool on interface", interface)
|
||||
sys.exit(1)
|
||||
|
||||
def parse_ethtool_output(lines, keys):
|
||||
result = {}
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
key = line.split(':', 1)[0].strip()
|
||||
if key in keys:
|
||||
key, value = line.split(':', 1)
|
||||
result[key.strip()] = value.strip()
|
||||
return result
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Usage: {sys.argv[0]} INTERFACE")
|
||||
sys.exit(1)
|
||||
|
||||
interface = sys.argv[1]
|
||||
keys = ["Duplex", "Auto-negotiation"]
|
||||
|
||||
lines = get_ethtool_output(interface)
|
||||
parsed_data = parse_ethtool_output(lines, keys)
|
||||
|
||||
print(json.dumps(parsed_data, indent=4))
|
||||
@@ -40,10 +40,13 @@ class DTSystem:
|
||||
continue
|
||||
|
||||
ph, = struct.unpack(">L", open(phandle, "rb").read())
|
||||
sys[ph] = root
|
||||
if ph not in sys:
|
||||
sys[ph] = []
|
||||
sys[ph].append(root)
|
||||
|
||||
phs = set(list(dt.keys()) + list(sys.keys()))
|
||||
self.devs = { ph: Device(ph, dt.get(ph), sys.get(ph)) for ph in phs }
|
||||
|
||||
self.devs = { ph: [Device(ph, dt.get(ph), s if s is not None else "") for s in (sys.get(ph) or []) if ph is not None] for ph in phs }
|
||||
self.base = Device(0, None, DTSystem.BASE)
|
||||
self.infix = Device(0, None, DTSystem.INFIX)
|
||||
|
||||
@@ -56,7 +59,7 @@ class DTSystem:
|
||||
elems = len(data) // struct.calcsize(">L")
|
||||
return struct.unpack(">" + elems * "L", data)
|
||||
|
||||
def device_from_ph(self, ph):
|
||||
def devices_from_ph(self, ph):
|
||||
return self.devs.get(ph)
|
||||
|
||||
def into_vpd(self, dev):
|
||||
@@ -79,12 +82,23 @@ class DTSystem:
|
||||
"data": parse(),
|
||||
}
|
||||
|
||||
def infix_usb_devices(self, out):
|
||||
names=self.infix.str_array("usb-port-names")
|
||||
phs=self.__get_phandle_array("usb-ports")
|
||||
data=dict(zip(names,phs))
|
||||
if data != {}:
|
||||
out["usb-ports"] = []
|
||||
for name,ph in data.items():
|
||||
[out["usb-ports"].extend([{"name": name, "path": dev.attrpath("authorized")}, {"name": name, "path": dev.attrpath("authorized_default")}]) for dev in self.devices_from_ph(ph)]
|
||||
|
||||
|
||||
def infix_devices(self, kind):
|
||||
phs = self.__get_phandle_array(kind)
|
||||
return [self.device_from_ph(ph) for ph in phs]
|
||||
return [[d for d in self.devices_from_ph(ph)] for ph in phs]
|
||||
|
||||
def infix_vpds(self):
|
||||
return [self.into_vpd(dev) for dev in self.infix_devices("vpds")]
|
||||
flat_devices = [device for sublist in self.infix_devices("vpds") for device in sublist]
|
||||
return [self.into_vpd(device) for device in flat_devices]
|
||||
|
||||
class QEMUSystem:
|
||||
BASE = "/sys/firmware/qemu_fw_cfg"
|
||||
@@ -109,6 +123,28 @@ class QEMUSystem:
|
||||
def vpds(self):
|
||||
return [self.product_vpd()]
|
||||
|
||||
def usb_ports(self):
|
||||
ports = [
|
||||
{
|
||||
"name": "USB",
|
||||
"path": "/sys/bus/usb/devices/usb1/authorized"
|
||||
|
||||
},
|
||||
{
|
||||
"name": "USB",
|
||||
"path": "/sys/bus/usb/devices/usb1/authorized_default"
|
||||
|
||||
},
|
||||
{
|
||||
"name": "USB2",
|
||||
"path": "/sys/bus/usb/devices/usb2/authorized"
|
||||
},
|
||||
{
|
||||
"name": "USB2",
|
||||
"path": "/sys/bus/usb/devices/usb2/authorized_default"
|
||||
|
||||
}]
|
||||
return ports
|
||||
class Device:
|
||||
def __init__(self, ph, dtpath, syspath):
|
||||
self.ph, self.dtpath, self.syspath = ph, dtpath, syspath
|
||||
@@ -143,6 +179,9 @@ class Device:
|
||||
val = self.attr(attr)
|
||||
return val.decode("utf-8").strip("\0") if val else default
|
||||
|
||||
def str_array(self, attr, default=None):
|
||||
val = self.attr(attr)
|
||||
return val.decode("utf-8").strip("\0").split("\0") if val else default
|
||||
|
||||
def dtattrpath(self, attr):
|
||||
return os.path.join(self.dtpath, attr)
|
||||
@@ -199,8 +238,9 @@ def probe_qemusystem(out):
|
||||
|
||||
qsys = QEMUSystem()
|
||||
vpds = qsys.vpds()
|
||||
usb_ports = qsys.usb_ports()
|
||||
vpd_inject(out, vpds)
|
||||
|
||||
out["usb-ports"] = usb_ports
|
||||
for (attr, default) in (
|
||||
("vendor", "QEMU"),
|
||||
("product-name", "VM"),
|
||||
@@ -221,7 +261,7 @@ def probe_qemusystem(out):
|
||||
def probe_dtsystem(out):
|
||||
dtsys = DTSystem()
|
||||
vpds = dtsys.infix_vpds()
|
||||
|
||||
dtsys.infix_usb_devices(out)
|
||||
model = dtsys.base.str("model")
|
||||
if model:
|
||||
out["product-name"] = model
|
||||
|
||||
@@ -10,17 +10,15 @@ test-env = $(test-dir)/env \
|
||||
test-env-qeneth = $(call test-env,-q $(test-dir)/virt/quad,$(1))
|
||||
test-env-run = $(call test-env,-C -t $(BINARIES_DIR)/qemu.dot,$(1))
|
||||
|
||||
.PHONY: test-%
|
||||
|
||||
test-unit:
|
||||
$(test-dir)/env $(test-dir)/9pm/9pm.py $(UNIT_TESTS)
|
||||
|
||||
test-qeneth:
|
||||
test test-qeneth:
|
||||
$(call test-env-qeneth,\
|
||||
$(BR2_EXTERNAL_INFIX_PATH)/test/9pm/9pm.py \
|
||||
$(INFIX_TESTS))
|
||||
test-qeneth-sh:
|
||||
$(call test-env-qeneth,/bin/sh)
|
||||
test-sh test-qeneth-sh:
|
||||
$(call test-env-qeneth,-i /bin/sh)
|
||||
|
||||
test-run: | ~/.infix-test-venv
|
||||
$(call test-env-run,\
|
||||
@@ -34,3 +32,5 @@ test-run-play: | ~/.infix-test-venv
|
||||
|
||||
~/.infix-test-venv:
|
||||
$(test-dir)/docker/init-venv.sh $(test-dir)/docker/pip-requirements.txt
|
||||
|
||||
.PHONY: test-unit test test-sh test-qeneth test-qeneth-sh test-run test-run-sh test-run-play
|
||||
|
||||
@@ -38,10 +38,8 @@ CONFIG_MODULES=y
|
||||
CONFIG_MODULE_UNLOAD=y
|
||||
CONFIG_NET=y
|
||||
CONFIG_PACKET=y
|
||||
CONFIG_UNIX=y
|
||||
CONFIG_XDP_SOCKETS=y
|
||||
CONFIG_XDP_SOCKETS_DIAG=y
|
||||
CONFIG_INET=y
|
||||
CONFIG_IP_MULTICAST=y
|
||||
CONFIG_IP_ADVANCED_ROUTER=y
|
||||
CONFIG_IP_MULTIPLE_TABLES=y
|
||||
@@ -83,7 +81,6 @@ CONFIG_NFT_MASQ=m
|
||||
CONFIG_NFT_REDIR=m
|
||||
CONFIG_NFT_NAT=m
|
||||
CONFIG_NFT_TUNNEL=m
|
||||
CONFIG_NFT_OBJREF=m
|
||||
CONFIG_NFT_QUEUE=m
|
||||
CONFIG_NFT_REJECT=m
|
||||
CONFIG_NFT_COMPAT=m
|
||||
@@ -156,9 +153,7 @@ CONFIG_VLAN_8021Q=y
|
||||
CONFIG_VLAN_8021Q_GVRP=y
|
||||
CONFIG_VLAN_8021Q_MVRP=y
|
||||
CONFIG_NET_SCHED=y
|
||||
CONFIG_NET_SCH_CBQ=y
|
||||
CONFIG_NET_CLS_BASIC=y
|
||||
CONFIG_NET_CLS_TCINDEX=y
|
||||
CONFIG_NET_CLS_BPF=y
|
||||
CONFIG_NET_CLS_FLOWER=y
|
||||
CONFIG_NETLINK_DIAG=y
|
||||
@@ -224,11 +219,11 @@ CONFIG_WATCHDOG=y
|
||||
CONFIG_WATCHDOG_SYSFS=y
|
||||
CONFIG_SOFT_WATCHDOG=y
|
||||
CONFIG_I6300ESB_WDT=y
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_XHCI_HCD=y
|
||||
CONFIG_USB_EHCI_HCD=y
|
||||
CONFIG_USB_UHCI_HCD=y
|
||||
CONFIG_USB_STORAGE=y
|
||||
CONFIG_USB=m
|
||||
CONFIG_USB_XHCI_HCD=m
|
||||
CONFIG_USB_EHCI_HCD=m
|
||||
CONFIG_USB_UHCI_HCD=m
|
||||
CONFIG_USB_STORAGE=m
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
CONFIG_LEDS_TRIGGERS=y
|
||||
@@ -246,7 +241,6 @@ CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_BTRFS_FS=y
|
||||
CONFIG_BTRFS_FS_POSIX_ACL=y
|
||||
CONFIG_AUTOFS4_FS=y
|
||||
CONFIG_FUSE_FS=y
|
||||
CONFIG_VIRTIO_FS=y
|
||||
CONFIG_OVERLAY_FS=y
|
||||
@@ -260,8 +254,8 @@ CONFIG_SQUASHFS_ZSTD=y
|
||||
CONFIG_9P_FS=y
|
||||
CONFIG_NLS_CODEPAGE_437=y
|
||||
CONFIG_NLS_ISO8859_1=y
|
||||
CONFIG_CRYPTO_GCM=y
|
||||
CONFIG_CRYPTO_AES=y
|
||||
CONFIG_CRYPTO_GCM=y
|
||||
CONFIG_DEBUG_KERNEL=y
|
||||
CONFIG_MAGIC_SYSRQ=y
|
||||
CONFIG_DEBUG_FS=y
|
||||
|
||||
@@ -14,6 +14,7 @@ BR2_INIT_FINIT=y
|
||||
BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y
|
||||
BR2_ROOTFS_DEVICE_TABLE="system/device_table.txt ${BR2_EXTERNAL_INFIX_PATH}/board/common/xattrs ${BR2_EXTERNAL_INFIX_PATH}/board/netconf/xattrs"
|
||||
# BR2_TARGET_ENABLE_ROOT_LOGIN is not set
|
||||
BR2_SYSTEM_BIN_SH_BASH=y
|
||||
BR2_TARGET_GENERIC_GETTY_TERM="xterm"
|
||||
BR2_SYSTEM_DHCP="eth0"
|
||||
BR2_ENABLE_LOCALE_WHITELIST="C en_US en_CA"
|
||||
@@ -33,7 +34,6 @@ BR2_LINUX_KERNEL_CUSTOM_DTS_OVERLAY="${BR2_EXTERNAL_INFIX_PATH}/board/aarch64/dt
|
||||
BR2_LINUX_KERNEL_DTB_KEEP_DIRNAME=y
|
||||
BR2_LINUX_KERNEL_INSTALL_TARGET=y
|
||||
BR2_PACKAGE_BUSYBOX_CONFIG="${BR2_EXTERNAL_INFIX_PATH}/board/common/busybox_defconfig"
|
||||
BR2_PACKAGE_BUSYBOX_SHOW_OTHERS=y
|
||||
BR2_PACKAGE_STRACE=y
|
||||
BR2_PACKAGE_STRESS_NG=y
|
||||
BR2_PACKAGE_JQ=y
|
||||
@@ -53,7 +53,9 @@ BR2_PACKAGE_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_FIT_CHECK_SIGN=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_MKENVIMAGE=y
|
||||
BR2_PACKAGE_PYTHON3=y
|
||||
BR2_PACKAGE_CA_CERTIFICATES=y
|
||||
BR2_PACKAGE_LIBSSH_OPENSSL=y
|
||||
BR2_PACKAGE_LIBSSH2=y
|
||||
BR2_PACKAGE_LIBSSH2_OPENSSL=y
|
||||
BR2_PACKAGE_LIBOPENSSL_BIN=y
|
||||
BR2_PACKAGE_LIBCURL_CURL=y
|
||||
BR2_PACKAGE_NETOPEER2_CLI=y
|
||||
@@ -70,7 +72,6 @@ BR2_PACKAGE_FPING=y
|
||||
BR2_PACKAGE_FRR=y
|
||||
# BR2_PACKAGE_IFUPDOWN_SCRIPTS is not set
|
||||
BR2_PACKAGE_IPROUTE2=y
|
||||
BR2_PACKAGE_IPTABLES=y
|
||||
BR2_PACKAGE_IPTABLES_NFTABLES=y
|
||||
BR2_PACKAGE_IPUTILS=y
|
||||
BR2_PACKAGE_LLDPD=y
|
||||
@@ -88,10 +89,9 @@ BR2_PACKAGE_SOCAT=y
|
||||
BR2_PACKAGE_TCPDUMP=y
|
||||
BR2_PACKAGE_TRACEROUTE=y
|
||||
BR2_PACKAGE_ULOGD=y
|
||||
BR2_PACKAGE_BASH=y
|
||||
BR2_PACKAGE_BASH_COMPLETION=y
|
||||
BR2_PACKAGE_HTOP=y
|
||||
BR2_PACKAGE_SUDO=y
|
||||
BR2_PACKAGE_HTOP=y
|
||||
BR2_PACKAGE_IRQBALANCE=y
|
||||
BR2_PACKAGE_KMOD_TOOLS=y
|
||||
BR2_PACKAGE_PWGEN=y
|
||||
@@ -121,6 +121,8 @@ INFIX_HOME="https://github.com/kernelkit/infix/"
|
||||
INFIX_DOC="https://github.com/kernelkit/infix/tree/main/doc"
|
||||
INFIX_SUPPORT="mailto:kernelkit@googlegroups.com"
|
||||
BR2_PACKAGE_CONFD=y
|
||||
BR2_PACKAGE_CURIOS_HTTPD=y
|
||||
BR2_PACKAGE_EXECD=y
|
||||
BR2_PACKAGE_STATD=y
|
||||
BR2_PACKAGE_FACTORY=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_HOTPLUG=y
|
||||
@@ -130,9 +132,14 @@ BR2_PACKAGE_FINIT_PLUGIN_RTC=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_TTY=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_URANDOM=y
|
||||
BR2_PACKAGE_IITO=y
|
||||
BR2_PACKAGE_K8S_LOGGER=y
|
||||
BR2_PACKAGE_KEYACK=y
|
||||
BR2_PACKAGE_KLISH_PLUGIN_INFIX=y
|
||||
BR2_PACKAGE_LOWDOWN=y
|
||||
BR2_PACKAGE_PODMAN=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_BTRFS=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_DEVICEMAPPER=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_VFS=y
|
||||
BR2_PACKAGE_TETRIS=y
|
||||
BR2_PACKAGE_QUERIERD=y
|
||||
BR2_PACKAGE_LIBINPUT=y
|
||||
|
||||
@@ -15,6 +15,7 @@ BR2_INIT_FINIT=y
|
||||
BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y
|
||||
BR2_ROOTFS_DEVICE_TABLE="system/device_table.txt ${BR2_EXTERNAL_INFIX_PATH}/board/common/xattrs ${BR2_EXTERNAL_INFIX_PATH}/board/netconf/xattrs"
|
||||
# BR2_TARGET_ENABLE_ROOT_LOGIN is not set
|
||||
BR2_SYSTEM_BIN_SH_BASH=y
|
||||
BR2_TARGET_GENERIC_GETTY_TERM="xterm"
|
||||
BR2_SYSTEM_DHCP="eth0"
|
||||
BR2_ENABLE_LOCALE_WHITELIST="C en_US en_CA"
|
||||
@@ -63,6 +64,9 @@ BR2_PACKAGE_UBOOT_TOOLS_FIT_CHECK_SIGN=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_MKENVIMAGE=y
|
||||
BR2_PACKAGE_PYTHON3=y
|
||||
BR2_PACKAGE_CA_CERTIFICATES=y
|
||||
BR2_PACKAGE_LIBSSH_OPENSSL=y
|
||||
BR2_PACKAGE_LIBSSH2=y
|
||||
BR2_PACKAGE_LIBSSH2_OPENSSL=y
|
||||
BR2_PACKAGE_LIBOPENSSL_BIN=y
|
||||
BR2_PACKAGE_LIBCURL_CURL=y
|
||||
BR2_PACKAGE_NETOPEER2_CLI=y
|
||||
@@ -147,6 +151,7 @@ INFIX_HOME="https://github.com/kernelkit/infix/"
|
||||
INFIX_DOC="https://github.com/kernelkit/infix/tree/main/doc"
|
||||
INFIX_SUPPORT="mailto:kernelkit@googlegroups.com"
|
||||
BR2_PACKAGE_CONFD=y
|
||||
BR2_PACKAGE_EXECD=y
|
||||
BR2_PACKAGE_STATD=y
|
||||
BR2_PACKAGE_FACTORY=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_HOTPLUG=y
|
||||
@@ -156,9 +161,16 @@ BR2_PACKAGE_FINIT_PLUGIN_RTC=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_TTY=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_URANDOM=y
|
||||
BR2_PACKAGE_IITO=y
|
||||
BR2_PACKAGE_K8S_LOGGER=y
|
||||
BR2_PACKAGE_KEYACK=y
|
||||
BR2_PACKAGE_KLISH_PLUGIN_INFIX=y
|
||||
BR2_PACKAGE_LOWDOWN=y
|
||||
BR2_PACKAGE_PODMAN=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_BTRFS=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_DEVICEMAPPER=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_VFS=y
|
||||
BR2_PACKAGE_TETRIS=y
|
||||
BR2_PACKAGE_QUERIERD=y
|
||||
BR2_PACKAGE_LIBINPUT=y
|
||||
# SIGN_ENABLED is not set
|
||||
# GNS3_APPLIANCE is not set
|
||||
|
||||
@@ -13,6 +13,7 @@ BR2_INIT_FINIT=y
|
||||
BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y
|
||||
BR2_ROOTFS_DEVICE_TABLE="system/device_table.txt ${BR2_EXTERNAL_INFIX_PATH}/board/common/xattrs ${BR2_EXTERNAL_INFIX_PATH}/board/netconf/xattrs"
|
||||
# BR2_TARGET_ENABLE_ROOT_LOGIN is not set
|
||||
BR2_SYSTEM_BIN_SH_BASH=y
|
||||
BR2_TARGET_GENERIC_GETTY_TERM="xterm"
|
||||
BR2_SYSTEM_DHCP="eth0"
|
||||
BR2_ENABLE_LOCALE_WHITELIST="C en_US en_CA"
|
||||
@@ -29,7 +30,6 @@ BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE="${BR2_EXTERNAL_INFIX_PATH}/board/x86_64/lin
|
||||
BR2_LINUX_KERNEL_INSTALL_TARGET=y
|
||||
BR2_LINUX_KERNEL_NEEDS_HOST_LIBELF=y
|
||||
BR2_PACKAGE_BUSYBOX_CONFIG="${BR2_EXTERNAL_INFIX_PATH}/board/common/busybox_defconfig"
|
||||
BR2_PACKAGE_BUSYBOX_SHOW_OTHERS=y
|
||||
BR2_PACKAGE_STRACE=y
|
||||
BR2_PACKAGE_STRESS_NG=y
|
||||
BR2_PACKAGE_JQ=y
|
||||
@@ -48,7 +48,9 @@ BR2_PACKAGE_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_FIT_CHECK_SIGN=y
|
||||
BR2_PACKAGE_UBOOT_TOOLS_MKENVIMAGE=y
|
||||
BR2_PACKAGE_PYTHON3=y
|
||||
BR2_PACKAGE_CA_CERTIFICATES=y
|
||||
BR2_PACKAGE_LIBSSH_OPENSSL=y
|
||||
BR2_PACKAGE_LIBSSH2=y
|
||||
BR2_PACKAGE_LIBSSH2_OPENSSL=y
|
||||
BR2_PACKAGE_LIBOPENSSL_BIN=y
|
||||
BR2_PACKAGE_LIBCURL_CURL=y
|
||||
BR2_PACKAGE_NETOPEER2_CLI=y
|
||||
@@ -65,7 +67,6 @@ BR2_PACKAGE_FPING=y
|
||||
BR2_PACKAGE_FRR=y
|
||||
# BR2_PACKAGE_IFUPDOWN_SCRIPTS is not set
|
||||
BR2_PACKAGE_IPROUTE2=y
|
||||
BR2_PACKAGE_IPTABLES=y
|
||||
BR2_PACKAGE_IPTABLES_NFTABLES=y
|
||||
BR2_PACKAGE_IPUTILS=y
|
||||
BR2_PACKAGE_LLDPD=y
|
||||
@@ -83,10 +84,9 @@ BR2_PACKAGE_SOCAT=y
|
||||
BR2_PACKAGE_TCPDUMP=y
|
||||
BR2_PACKAGE_TRACEROUTE=y
|
||||
BR2_PACKAGE_ULOGD=y
|
||||
BR2_PACKAGE_BASH=y
|
||||
BR2_PACKAGE_BASH_COMPLETION=y
|
||||
BR2_PACKAGE_HTOP=y
|
||||
BR2_PACKAGE_SUDO=y
|
||||
BR2_PACKAGE_HTOP=y
|
||||
BR2_PACKAGE_IRQBALANCE=y
|
||||
BR2_PACKAGE_KMOD_TOOLS=y
|
||||
BR2_PACKAGE_PWGEN=y
|
||||
@@ -124,6 +124,8 @@ INFIX_HOME="https://github.com/kernelkit/infix/"
|
||||
INFIX_DOC="https://github.com/kernelkit/infix/tree/main/doc"
|
||||
INFIX_SUPPORT="mailto:kernelkit@googlegroups.com"
|
||||
BR2_PACKAGE_CONFD=y
|
||||
BR2_PACKAGE_CURIOS_HTTPD=y
|
||||
BR2_PACKAGE_EXECD=y
|
||||
BR2_PACKAGE_STATD=y
|
||||
BR2_PACKAGE_FACTORY=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_HOTPLUG=y
|
||||
@@ -133,9 +135,14 @@ BR2_PACKAGE_FINIT_PLUGIN_RTC=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_TTY=y
|
||||
BR2_PACKAGE_FINIT_PLUGIN_URANDOM=y
|
||||
BR2_PACKAGE_IITO=y
|
||||
BR2_PACKAGE_K8S_LOGGER=y
|
||||
BR2_PACKAGE_KEYACK=y
|
||||
BR2_PACKAGE_KLISH_PLUGIN_INFIX=y
|
||||
BR2_PACKAGE_LOWDOWN=y
|
||||
BR2_PACKAGE_PODMAN=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_BTRFS=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_DEVICEMAPPER=y
|
||||
BR2_PACKAGE_PODMAN_DRIVER_VFS=y
|
||||
BR2_PACKAGE_TETRIS=y
|
||||
BR2_PACKAGE_QUERIERD=y
|
||||
GNS3_APPLIANCE_RAM=512
|
||||
|
||||
+221
-1
@@ -4,6 +4,224 @@ Change Log
|
||||
All notable changes to the project are documented in this file.
|
||||
|
||||
|
||||
[v24.02.0][] - 2024-03-01
|
||||
-------------------------
|
||||
|
||||
> **Note:** the `root` account is disabled in official builds. Only the
|
||||
> `admin` user can log in to the system. This can be changed, but only
|
||||
> in developer builds: `make menuconfig` -> System configuration ->
|
||||
> `[*]Enable root login with password`
|
||||
|
||||
### YANG Status
|
||||
|
||||
Infix devices support downloading all YANG models over NETCONF, including
|
||||
models with submodules. As a rule, standard models are used as long as
|
||||
they map to underlying Linux concepts and services. All exceptions are
|
||||
listed in Infix specific models, detailing deviations and augmentations.
|
||||
|
||||
Currently supported models:
|
||||
|
||||
- [ieee802-ethernet-interface][]:
|
||||
- Toggle port speed & duplex auto-negotiation on/off
|
||||
- Set port speed and duplex when auto-negotiation is off
|
||||
- Query port speed/duplex and auto-negotiation status (operational)
|
||||
- **Frame counters:**
|
||||
|
||||
| **YANG** | **Linux / Ethtool** |
|
||||
|-----------------------------|-----------------------------------|
|
||||
| `out-frames` | `FramesTransmittedOK` |
|
||||
| `out-multicast-frames` | `MulticastFramesXmittedOK` |
|
||||
| `out-broadcast-frames` | `BroadcastFramesXmittedOK` |
|
||||
| `in-total-octets` | `FramesReceivedOK` |
|
||||
| | + `FrameCheckSequenceErrors` |
|
||||
| | + `FramesLostDueToIntMACRcvError` |
|
||||
| | + `AlignmentErrors` |
|
||||
| | + `etherStatsOversizePkts` |
|
||||
| | + `etherStatsJabbers` |
|
||||
| `in-frames` | `FramesReceivedOK` |
|
||||
| `in-multicast-frames` | `MulticastFramesReceivedOK` |
|
||||
| `in-broadcast-frames` | `BroadcastFramesReceivedOK` |
|
||||
| `in-error-undersize-frames` | `undersize_pkts` |
|
||||
| `in-error-fcs-frames` | `FrameCheckSequenceErrors` |
|
||||
| `in-good-octets` | `OctetsReceivedOK` |
|
||||
| `out-good-octets` | `OctetsTransmittedOK` |
|
||||
|
||||
- [ietf-hardware][]:
|
||||
- Populates standard hardware model from corresponding data in device EEPROMs
|
||||
- **augments:**
|
||||
- Initial support for USB ports
|
||||
- Vital Product Data (VPD) from device EEPROMs ([ONIE][] structure)
|
||||
- [infix-hardware][]: Deviations and augments
|
||||
- [ietf-system][]:
|
||||
- **augments:**
|
||||
- Message of the Day (MotD) banner, shown after SSH or console login.
|
||||
Please note: the legacy `motd` has been replaced with `motd-banner` os
|
||||
of v24.02. Use CLI `text-editor` to modify the latter
|
||||
- User login shell, default: `/bin/false` (no SSH or console login)
|
||||
- State information for remotely querying firmware version information
|
||||
- **deviations:**
|
||||
- timezone-name, using IANA timezones instead of plain string
|
||||
- UTC offset, only support per-hour offsets with [tzdata][]
|
||||
- Usernames, clarifying Linux restrictions
|
||||
- Unsupported features marked as deviations, e.g. RADIUS
|
||||
- [infix-system-software][]: firmware upgrade with `install-bundle` RPC
|
||||
- [ietf-interfaces][]:
|
||||
- deviation to allow read-write `if:phys-address` for custom MAC address
|
||||
- [ietf-ip][]: augments
|
||||
- IPv4LL similar to standardized IPv6LL
|
||||
- [ietf-ip][]: deviations (`not-supported`) added for IPv4 and IPv6:
|
||||
- `/if:interfaces/if:interface/ip:ipv4/ip:address/ip:subnet/ip:netmask`
|
||||
- `/if:interfaces/if:interface/ip:ipv6/ip:address/ip:status`
|
||||
- `/if:interfaces/if:interface/ip:ipv4/ip:neighbor`
|
||||
- `/if:interfaces/if:interface/ip:ipv6/ip:neighbor`
|
||||
- [ietf-routing][]: Base model for routing
|
||||
- [ietf-ipv4-unicast-routing][]: Static unicast routing, incl. operational
|
||||
data, i.e., setting static IPv4 routes and reading IPv4 routing table
|
||||
- [ietf-ipv6-unicast-routing][]: Static unicast routing, incl. operational
|
||||
data, i.e., setting static IPv6 routes and reading IPv6 routing table
|
||||
- [ietf-ospf][]: Limited support for OSPFv2, with additional support for
|
||||
injecting default route, and route redistribution. Underlying routing
|
||||
engine in use is Frr. Includes operational status + data (routes).
|
||||
See [infix-routing][] model for detailed list of deviations
|
||||
- [infix-ethernet-interface][]: deviations for ieee802-ethernet-interface
|
||||
- [infix-routing][]: Limit ietf-routing to one instance `default` per
|
||||
routing protocol, also details unsupported features (deviations) to both
|
||||
ietf-routing and ietf-ospf models, as well as augments made to support
|
||||
injecting default route in OSPFv2
|
||||
- [infix-if-bridge][]: Linux bridge interfaces with native VLAN support
|
||||
- [infix-if-type][]: deviation for interface types, limiting number to
|
||||
supported types only. New identities are derived from default IANA
|
||||
interface types, ensuring compatibility with other standard models, e.g.,
|
||||
`ieee802-ethernet-interface.yang`
|
||||
- [infix-if-veth][]: Linux VETH pairs
|
||||
- [infix-if-vlan][]: Linux VLAN interfaces, e.g. `eth0.10`
|
||||
- [infix-containers][]: Support for Docker containers, incl. operational data
|
||||
to query status and remotely stop/start containers
|
||||
- [infix-dhcp-client][]: DHCPv4 client, including supported options
|
||||
- **Configurable services:**
|
||||
- [ieee802-dot1ab-lldp][]: stripped down to an `enabled` setting
|
||||
- [infix-services][]: support for enabling mDNS service/device discovery
|
||||
|
||||
[tzdata]: https://www.iana.org/time-zones
|
||||
[ietf-system]: https://www.rfc-editor.org/rfc/rfc7317.html
|
||||
[ietf-interfaces]: https://www.rfc-editor.org/rfc/rfc7223.html
|
||||
[ietf-ip]: https://www.rfc-editor.org/rfc/rfc8344.html
|
||||
[ietf-if-vlan-encapsulation]: https://www.ietf.org/id/draft-ietf-netmod-sub-intf-vlan-model-08.html
|
||||
[ietf-routing]: https://www.rfc-editor.org/rfc/rfc8349
|
||||
[ietf-ipv4-unicast-routing]: https://www.rfc-editor.org/rfc/rfc8349#page-29
|
||||
[ietf-ipv6-unicast-routing]: https://www.rfc-editor.org/rfc/rfc8349#page-37
|
||||
[ietf-hardware]: https://www.rfc-editor.org/rfc/rfc8348
|
||||
[ietf-ospf]: https://www.rfc-editor.org/rfc/rfc9129
|
||||
[ieee802-dot1ab-lldp]: https://github.com/kernelkit/infix/blob/f0c23ca/src/confd/yang/ieee802-dot1ab-lldp%402022-03-15.yang
|
||||
[ieee802-ethernet-interface]: https://github.com/kernelkit/infix/blob/f0c23ca/src/confd/yang/ieee802-ethernet-interface%402019-06-21.yang
|
||||
[infix-ethernet-interface]: https://github.com/kernelkit/infix/blob/f0c23ca/src/confd/yang/infix-ethernet-interface%402024-02-27.yang
|
||||
[infix-containers]: https://github.com/kernelkit/infix/blob/f0c23ca/src/confd/yang/infix-containers%402024-02-01.yang
|
||||
[infix-dhcp-client]: https://github.com/kernelkit/infix/blob/f0c23ca/src/confd/yang/infix-dhcp-client%402024-01-30.yang
|
||||
[infix-hardware]: https://github.com/kernelkit/infix/blob/f0c23ca/src/confd/yang/infix-hardware%402024-01-18.yang
|
||||
[infix-if-bridge]: https://github.com/kernelkit/infix/blob/f0c23ca/src/confd/yang/infix-if-bridge%402024-02-19.yang
|
||||
[infix-if-type]: https://github.com/kernelkit/infix/blob/f0c23ca/src/confd/yang/infix-if-type%402024-01-29.yang
|
||||
[infix-if-veth]: https://github.com/kernelkit/infix/blob/f0c23ca/src/confd/yang/infix-if-veth%402023-06-05.yang
|
||||
[infix-if-vlan]: https://github.com/kernelkit/infix/blob/f0c23ca/src/confd/yang/infix-if-vlan%402023-10-25.yang
|
||||
[infix-ip]: https://github.com/kernelkit/infix/tree/f0c23ca/src/confd/yang/infix-ip%402023-09-14.yang
|
||||
[infix-routing]: https://github.com/kernelkit/infix/blob/f0c23ca/src/confd/yang/infix-routing%402024-01-09.yang
|
||||
[infix-services]: https://github.com/kernelkit/infix/blob/f0c23ca/src/confd/yang/infix-services%402023-10-16.yang
|
||||
[infix-system-software]: https://github.com/kernelkit/infix/tree/f0c23ca/src/confd/yang/infix-system-software%402023-06-27.yang
|
||||
[ONIE]: https://opencomputeproject.github.io/onie/design-spec/hw_requirements.html
|
||||
|
||||
### Changes
|
||||
|
||||
- New hardware support: NanoPi R2S from FriendlyELEC, a simple two-port router
|
||||
- Static routing support, now also for IPv6
|
||||
- Dynamic routing support with OSPFv2, limited (see `infix-routing.yang` for
|
||||
deviations), but still usable in most relevant use-cases. If you are using
|
||||
this and are interested in more features, please let us know!
|
||||
- Multiple area support, including different area types
|
||||
- Route redistribution
|
||||
- Default route injection
|
||||
- Full integration with Bidirectional Forward Detection (BFD)
|
||||
- Operational status, including but not limited to:
|
||||
* OSPF Router ID
|
||||
* Neighbor status
|
||||
* OSPF routing table
|
||||
* Interface type, incl. passive status
|
||||
- For more information, see `doc/networking.md`
|
||||
- Support for disabling USB ports in `startup-config` (no auto-mount yet!)
|
||||
- Initial support for Docker containers, see documentation for details:
|
||||
- Custom Infix model, see `infix-containers.yang` for details
|
||||
- Add image URL/location and volumes/mounts/interfaces to configuration,
|
||||
the system ensures the image is downloaded and container created in the
|
||||
background before launching it. If now networking is available the job
|
||||
is queued and retried every time a new network route is learned
|
||||
- Status and actions (stop/start/restart) available in operational datastore
|
||||
- Possible to move physical switch ports inside container, see docs
|
||||
- Possible to bundle OCI archives in Infix image, as well as storing any
|
||||
file content in `factory-config` to override container image defaults
|
||||
- IEEE Ethernet interface:
|
||||
- Support for setting port speed/duplex or auto-negotiating
|
||||
- New per-port counters, augments to IEEE model added in infix-ethernet.yang:
|
||||
`in-good-octets`, `out-good-octets`
|
||||
- Many updates to DHCPv4 client YANG model:
|
||||
- new options, see `infix-dhcp-client.yang` for details:
|
||||
- Default options: subnet, router, dns+domain, hostname, broadcast, ntpsrv
|
||||
- Set NTP servers, require NTP client in ietf-system to be enabled, will
|
||||
be treated as non-preferred sources, configured `prefer` servers wins
|
||||
- Learn DNS servers, statically configured servers always takes precedence
|
||||
- Install routes, not only from option 3, but also options 121 and 249
|
||||
- Support for ARP:ing for client lease (default enabled)
|
||||
- Configurable route metrics, by default metric 100 to allow static routes
|
||||
to win over DHCP routes, useful for backup DHCP connections
|
||||
- IETF Hardware data: added YANG model for vital product data representation,
|
||||
and augments for initial USB support (enable/disable)
|
||||
- IETF System:
|
||||
- the `motd` augment in `infix-system.yang` for *Message of the Day* has
|
||||
been marked as obsolete and replaced with `motd-banner`. The new setting
|
||||
is of type *binary* and allows control codes and multi-line content to be
|
||||
stored. The legacy `motd` will remain for the foreseeable future and
|
||||
takes precedence over the new `motd-banner` setting
|
||||
- new `text-editor` augment in `infix-system.yang` to select the backend for
|
||||
the new `text-editor` command: `emacs`, `nano`, or `vi`
|
||||
- Many updates to the test system, *Infamy*, incl. new Quick Start Guide in
|
||||
updated `doc/testing.md` to help new developers get started
|
||||
- Add `htop` to default builds, useful for observing and attaching (strace)
|
||||
- Change the default shell of the `admin` user from `clish` to `bash`. Change
|
||||
required for factory production and provisioning reasons. Only affects the
|
||||
built-in default, customer specific `factory-config`'s are not affected!
|
||||
- CLI: the `set` command on a boolean can now be used without an argument,
|
||||
`set boolean` sets the boolean option to true
|
||||
- CLI: new command `change`, for use with ietf-system user passwords, starts
|
||||
an interactive password dialog, including confirmation entry. The resulting
|
||||
password is by default salted and hashed using sha512crypt
|
||||
- CLI: new command `text-editor`, for use with binary fields, e.g., `content`
|
||||
for file mounts in containers, or the new `motd-banner`:
|
||||
|
||||
admin@infix-c0-ff-ee:/config/system/> text-editor motd-banner
|
||||
... exit with Ctrl-x Ctrl-c ...
|
||||
admin@infix-c0-ff-ee:/config/system/> show
|
||||
motd-banner VGhpcyByZWxlYXNlIHdhcyBzcG9uc29yZWQgYnkgQWRkaXZhIEVsZWt0cm9uaWsK;
|
||||
|
||||
- CLI: new admin-exec command `show ntp [sources]`
|
||||
- CLI: new admin-exec command `show dns` to display DNS client status
|
||||
- CLI: new admin-exec command `show ospf [subcommand]`
|
||||
- CLI: new admin-exec command `show container [subcommand]`
|
||||
- CLI: new admin-exec command `show hardware` only USB port status for now
|
||||
- CLI: updates to the `show interfaces` command to better list bridge VLANs
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fix #177: ensure bridge is not bridge port to itself
|
||||
- Fix #259: failure to `copy factory-config startup-config` in CLI
|
||||
- Fix #278: allow DHCP client to set system hostname (be careful)
|
||||
- Fix #283: hostname in DHCP request adds quotation marks
|
||||
- Fix #294: drop stray `v` from version suffix in release artifacts
|
||||
- Fix #298: drop privileges properly before launching user `shell` in CLI
|
||||
- Fix #312: race condition in `ipv4_autoconf.py`, causes test to block forever
|
||||
- Backport upstream fix to netopeer2-server for fetching YANG models that
|
||||
refer to submodules over NETCONF
|
||||
- CLI: drop developer debug in `set` command
|
||||
- Fix out-of-place `[OK]` messages at shutdown/reboot
|
||||
- Fix garbled syslog messages due to unicode in Infix tagline, drop unicode
|
||||
|
||||
|
||||
[v23.11.0][] - 2023-11-30
|
||||
-------------------------
|
||||
|
||||
@@ -478,7 +696,9 @@ Supported YANG models in addition to those used by sysrepo and netopeer:
|
||||
- N/A
|
||||
|
||||
[buildroot]: https://buildroot.org/
|
||||
[UNRELEASED]: https://github.com/kernelkit/infix/compare/v23.10.0...HEAD
|
||||
[UNRELEASED]: https://github.com/kernelkit/infix/compare/v24.02.0...HEAD
|
||||
[v24.02.0]: https://github.com/kernelkit/infix/compare/v23.11.0...v24.02.0
|
||||
[v23.11.0]: https://github.com/kernelkit/infix/compare/v23.10.0...v23.11.0
|
||||
[v23.10.0]: https://github.com/kernelkit/infix/compare/v23.09.0...v23.10.0
|
||||
[v23.09.0]: https://github.com/kernelkit/infix/compare/v23.08.0...v23.09.0
|
||||
[v23.08.0]: https://github.com/kernelkit/infix/compare/v23.06.0...v23.08.0
|
||||
|
||||
@@ -44,6 +44,8 @@ In *configure context* the following commands are available:
|
||||
| `set foo bar val` | Set `bar` leaf node in `foo` subcontext to `val` |
|
||||
| `no foo bar` | Clear/delete configuration made to `bar` in `foo` |
|
||||
| `edit foo baz` | Enter `baz` sub-sub-context in `foo` subcontext |
|
||||
| `change password` | Start password dialogue to change a user's password |
|
||||
| `text-editor foo` | Open a text editor to edit binary setting `foo` |
|
||||
| `abort` | Abort changes in configuration, return to admin-exec |
|
||||
| `exit` | Exit one level sub-context, or abort from top-level |
|
||||
| `leave` | Save changes to `running-config`, return to admin-exec |
|
||||
|
||||
@@ -21,5 +21,9 @@ keybindings are really useful to learn!
|
||||
| `help introduction` | An introduction to the CLI |
|
||||
| `help keybindings` | Lists keybindings and other helpful tricks |
|
||||
| `help tutorial` | The CLI User Guide |
|
||||
| `help text-editor` | Help with the built-in text-editor command |
|
||||
|
||||
> In `configure` context the `help setting` command shows the YANG
|
||||
> description text for each node and container. To reach the admin
|
||||
> exec help from configure context, e.g., `do help text-editor`
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# Text Editor
|
||||
|
||||
The CLI built-in `text-editor` command can be used to edit type `binary`
|
||||
settings in configure context.
|
||||
|
||||
The default editor is a Micro Emacs clone. Users not familiar with
|
||||
terminal based editors may benefit from this introduction.
|
||||
|
||||
|
||||
## Escape Meta/Alt Control Shift
|
||||
|
||||
When starting up, the editor status field at the bottom shows the
|
||||
following shorthand:
|
||||
|
||||
```
|
||||
C-h q quick help | C-h t tutorial | C-h b key bindings | C = Ctrl | M = Alt
|
||||
```
|
||||
|
||||
Key combinations with a `-` (dash) mean holding down the modifier key.
|
||||
Combinations without a `-` (dash) mean without any modifier key.
|
||||
|
||||
### Quick help `C-h q`
|
||||
|
||||
- hold down the `Ctrl` key on
|
||||
- tap the `h` key
|
||||
- release `Ctrl`
|
||||
- tap the `q` key
|
||||
|
||||
The bottom part of the terminal now shows a "buffer" called `*quick*`:
|
||||
|
||||
```
|
||||
FILE BUFFER WINDOW MARK/KILL MISC
|
||||
C-x C-c exit C-x b switch C-x 0 only other C-space mark C-_ undo
|
||||
C-x C-f find C-x k close C-x 1 only this C-w kill-rg C-s search
|
||||
C-x C-s save C-x C-b list C-x 2 split two C-k kill-ln C-r r-search
|
||||
C-x s save-all C-x h mark C-x ^ enlarge C-y yank M-% replace
|
||||
C-x i insert C-x g goto-ln C-x o other win C-x C-x swap M-q reformat
|
||||
```
|
||||
|
||||
### Save & Exit `C-x C-c`
|
||||
|
||||
- Hold down the Ctrl key
|
||||
- tap `X`
|
||||
- tap `c`
|
||||
- release `Ctrl`
|
||||
|
||||
> The status field at the bottom asks if you are really sure, and/or if
|
||||
> you want to add a final Enter/newline to the file. For binary content
|
||||
> that final newline may be important.
|
||||
|
||||
|
||||
## Changing the Editor
|
||||
|
||||
The system has three different built-in editors:
|
||||
|
||||
- `emacs` (Micro Emacs)
|
||||
- `nano` (GNU Nano)
|
||||
- `vi` (Visual Editor)
|
||||
|
||||
Changing editor is done in configure context, in the system container:
|
||||
|
||||
```
|
||||
admin@host:/> configure
|
||||
admin@host:/config/> edit system
|
||||
admin@host:/config/system/> set text-editor <TAB>
|
||||
emacs nano vi
|
||||
admin@host:/config/system/> set text-editor nano
|
||||
admin@host:/config/system/> leave
|
||||
admin@example:/>
|
||||
```
|
||||
+60
-28
@@ -1,12 +1,11 @@
|
||||
User Guide
|
||||
==========
|
||||
|
||||
The command line interface (CLI, see-ell-aye) is built on the open source
|
||||
component [klish][1], which implements a CISCO-like, or Juniper Networks
|
||||
JunOS-like, CLI for UNIX systems.
|
||||
The command line interface (CLI, see-ell-aye) implements a CISCO-like,
|
||||
or Juniper Networks JunOS-like, CLI for UNIX systems.
|
||||
|
||||
New users always get the CLI as the default "shell" when logging in, but
|
||||
the default `admin` user logs in to the Bash. To access the CLI, type:
|
||||
the default `admin` user logs in to `bash`. To access the CLI, type:
|
||||
|
||||
cli
|
||||
|
||||
@@ -104,6 +103,7 @@ the version we are currently running, intact in case of trouble.
|
||||
both partitions you must reboot to the new version (to verify it works)
|
||||
and then repeat the same command.
|
||||
|
||||
|
||||
Configure Context
|
||||
-----------------
|
||||
|
||||
@@ -113,21 +113,24 @@ followed by Enter. Available commands, press `?` at the prompt:
|
||||
```
|
||||
admin@host:/> configure
|
||||
admin@host:/config/>
|
||||
abort Abandon candidate
|
||||
check Validate candidate
|
||||
commit Commit current candidate to running-config
|
||||
delete Delete configuration setting(s)
|
||||
diff Summarize uncommitted changes
|
||||
do Execute operational mode command
|
||||
edit Descend to the specified configuration node
|
||||
exit Ascend to the parent configuration node, or abort (from top)
|
||||
leave Finalize candidate and apply to running-config
|
||||
no Alias for delete
|
||||
rollback Restore candidate to running-config
|
||||
set Set configuration setting
|
||||
show Show configuration
|
||||
top Ascend to the configuration root
|
||||
up Ascend to the parent configuration node
|
||||
abort Abandon candidate
|
||||
change Interactively change setting, e.g. password
|
||||
check Validate candidate
|
||||
commit Commit current candidate to running-config
|
||||
delete Delete configuration setting(s)
|
||||
diff Summarize uncommitted changes
|
||||
do Execute operational mode command
|
||||
edit Descend to the specified configuration node
|
||||
end Alias to up, leave this subsection/node
|
||||
exit Ascend to the parent configuration node, or abort (from top)
|
||||
leave Finalize candidate and apply to running-config
|
||||
no Alias for delete
|
||||
rollback Restore candidate to running-config
|
||||
set Set configuration setting
|
||||
show Show configuration
|
||||
text-editor Modify binary content in a text editor
|
||||
top Ascend to the configuration root
|
||||
up Ascend to the parent configuration node
|
||||
```
|
||||
|
||||
The `edit` command lets you change to a sub-configure context, e.g.:
|
||||
@@ -235,23 +238,52 @@ is committed.
|
||||
User management, including passwords, is also a part of `ietf-system`.
|
||||
|
||||
```
|
||||
admin@host:/config/> edit system authentication
|
||||
admin@host:/config/system/authentication/> do password encrypt
|
||||
Password: ******
|
||||
$1$oVHGR0AP$6Pad1Pm8jPwPsan5WHULS1
|
||||
admin@host:/config/system/authentication/> set user admin password $1$oVHGR0AP$6Pad1Pm8jPwPsan5WHULS1
|
||||
admin@host:/config/system/> leave
|
||||
admin@host:/config/> edit system authentication user admin
|
||||
admin@host:/config/system/authentication/user/admin/> change password
|
||||
New password:
|
||||
Retype password:
|
||||
admin@host:/config/system/authentication/user/admin/> leave
|
||||
```
|
||||
|
||||
The call to `do password encrypt` brings up the helpful admin-exec
|
||||
command to hash, and optionally salt, your password. This encrypted
|
||||
string is what goes into the system configuration.
|
||||
The `change password` command starts an interactive dialogue that asks
|
||||
for the new password, with a confirmation, and then salts and encrypts
|
||||
the password with sha512crypt.
|
||||
|
||||
It is also possible to use the `set password ...` command. This allows
|
||||
setting an already hashed password. To manually hash a password, use
|
||||
the `do password encrypt` command. This launches the admin-exec command
|
||||
to hash, and optionally salt, your password. This encrypted string can
|
||||
then be used with `set password ...`.
|
||||
|
||||
> **Tip:** if you are having trouble thinking of a password, there is
|
||||
> also `do password generate`, which generates random but readable
|
||||
> strings using the UNIX command `pwgen`.
|
||||
|
||||
|
||||
### SSH Authorized Key
|
||||
|
||||
Logging in remotely with SSH is possible by adding a *public key* to a
|
||||
user. Here we add the authorized key to the admin user, multiple keys
|
||||
are supported.
|
||||
|
||||
With SSH keys in place it is possible to disable password login, just
|
||||
remember to verify SSH login and network connectivity before doing so.
|
||||
|
||||
```
|
||||
admin@host:/config/> edit system authentication user admin
|
||||
admin@host:/config/system/authentication/user/admin/> edit authorized-key example@host
|
||||
admin@host:/config/system/authentication/user/admin/authorized-key/example@host/> set algorithm ssh-rsa
|
||||
admin@host:/config/system/authentication/user/admin/authorized-key/example@host/> set key-data AAAAB3NzaC1yc2EAAAADAQABAAABgQC8iBL42yeMBioFay7lty1C4ZDTHcHyo739gc91rTTH8SKvAE4g8Rr97KOz/8PFtOObBrE9G21K7d6UBuPqmd0RUF2CkXXN/eN2PBSHJ50YprRFt/z/304bsBYkDdflKlPDjuSmZ/+OMp4pTsq0R0eNFlX9wcwxEzooIb7VPEdvWE7AYoBRUdf41u3KBHuvjGd1M6QYJtbFLQMMTiVe5IUfyVSZ1RCxEyAB9fR9CBhtVheTVsY3iG0fZc9eCEo89ErDgtGUTJK4Hxt5yCNwI88YaVmkE85cNtw8YwubWQL3/tGZHfbbQ0fynfB4kWNloyRHFr7E1kDxuX5+pbv26EqRdcOVGucNn7hnGU6C1+ejLWdBD7vgsoilFrEaBWF41elJEPKDzpszEijQ9gTrrWeYOQ+x++lvmOdssDu4KvGmj2K/MQTL2jJYrMJ7GDzsUu3XikChRL7zNfS2jYYQLzovboUCgqfPUsVba9hqeX3U67GsJo+hy5MG9RSry4+ucHs=
|
||||
admin@host:/config/system/authentication/user/admin/authorized-key/example@host/> show
|
||||
algorithm ssh-rsa;
|
||||
key-data AAAAB3NzaC1yc2EAAAADAQABAAABgQC8iBL42yeMBioFay7lty1C4ZDTHcHyo739gc91rTTH8SKvAE4g8Rr97KOz/8PFtOObBrE9G21K7d6UBuPqmd0RUF2CkXXN/eN2PBSHJ50YprRFt/z/304bsBYkDdflKlPDjuSmZ/+OMp4pTsq0R0eNFlX9wcwxEzooIb7VPEdvWE7AYoBRUdf41u3KBHuvjGd1M6QYJtbFLQMMTiVe5IUfyVSZ1RCxEyAB9fR9CBhtVheTVsY3iG0fZc9eCEo89ErDgtGUTJK4Hxt5yCNwI88YaVmkE85cNtw8YwubWQL3/tGZHfbbQ0fynfB4kWNloyRHFr7E1kDxuX5+pbv26EqRdcOVGucNn7hnGU6C1+ejLWdBD7vgsoilFrEaBWF41elJEPKDzpszEijQ9gTrrWeYOQ+x++lvmOdssDu4KvGmj2K/MQTL2jJYrMJ7GDzsUu3XikChRL7zNfS2jYYQLzovboUCgqfPUsVba9hqeX3U67GsJo+hy5MG9RSry4+ucHs=;
|
||||
admin@host:/config/system/authentication/user/admin/authorized-key/example@host/> leave
|
||||
```
|
||||
|
||||
> **Note:** the `ssh-keygen` program already base64 encodes the public
|
||||
> key data, so there is no need to use the `text-editor` command, `set`
|
||||
> does the job.
|
||||
|
||||
### Creating a VETH Pair
|
||||
|
||||
The following example creates a `veth0a <--> veth0b` virtual Ethernet
|
||||
|
||||
+374
-254
@@ -2,302 +2,422 @@ Containers in Infix
|
||||
===================
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Docker Containers with Podman](#docker-containers-with-podman)
|
||||
* [Multiple Networks](#multiple-networks)
|
||||
* [Hybrid Mode](#hybrid-mode)
|
||||
* [Enabling Container Support](#enabling-container-support)
|
||||
* [Debugging Containers](#debugging-containers)
|
||||
* [Caution](#caution)
|
||||
* [Getting Started](#getting-started)
|
||||
* [Examples](#examples)
|
||||
* [Networking and Containers](#networking-and-containers)
|
||||
* [Container Bridge](#container-bridge)
|
||||
* [Container Host Interface](#container-host-interface)
|
||||
* [Host Networking](#host-networking)
|
||||
* [Mounts and Volumes](#mounts-and-volumes)
|
||||
* [Content Mounts](#content-mounts)
|
||||
* [Example Containers](#example-containers)
|
||||
* [System Container](#system-container)
|
||||
* [Application Container: nftables](#application-container--nftables)
|
||||
* [Application Container: ntpd](#application-container--ntpd)
|
||||
* [Upgrading a Container Image](#upgradeing-a-container-image)
|
||||
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Default builds of Infix do not enable any container support. See below
|
||||
section, [Enabling Container Support](#enabling-container-support), for
|
||||
details on how to enable it using Podman.
|
||||
Infix comes with native support for Docker containers using [podman][].
|
||||
The [YANG model][1] describes the current level of support, complete
|
||||
enough to run both system and application containers.
|
||||
|
||||
Networking in containers is provided by both Infix and the Container
|
||||
Network Interface ([CNI](https://www.cni.dev/)) that Podman supports.
|
||||
Key design features, like using Linux switchdev, allow users to assign
|
||||
switch ports directly to containers, not just bridged VETH pairs, this
|
||||
is a rare and in many cases *unique* feature of Infix.
|
||||
|
||||
> A convenience alias `docker=podman` is available in Infix, remember,
|
||||
> not all features or syntax of docker is available in podman.
|
||||
All network specific settings are done using the IETF interfaces YANG
|
||||
model, with augments for containers to ensure smooth integration with
|
||||
container networking in podman.
|
||||
|
||||
> **Note:** even though the `podman` command can be used directly from a
|
||||
> shell prompt, we strongly recommend using the CLI commands instead.
|
||||
> They employ the services of a wrapper `container` script which handles
|
||||
> the integration of containers in the system.
|
||||
|
||||
|
||||
Docker Containers with Podman
|
||||
-----------------------------
|
||||
Caution
|
||||
-------
|
||||
|
||||
We assume you've booted into Infix and start with a familiar example.
|
||||
This downloads the `hello-world` example container image and runs it:
|
||||
A word of warning, containers can run on your system in privileged mode,
|
||||
as `root`. This gives them full access to devices on your system. But
|
||||
even when though unprivileged containers are fenced from the host with
|
||||
Linux namespaces, and resource limited using Linux cgroups, which scope
|
||||
container applications from seeing and accessing the complete system,
|
||||
there is no guarantee that an application cannot ever break out of this
|
||||
confinement.
|
||||
|
||||
podman run -it --rm docker://hello-world
|
||||
- If the system is compromised, containers can be used to easily
|
||||
install malicious software in your system and over the network
|
||||
- Your system is as secure as anything you run in the container
|
||||
- If you run containers, there is no security guarantee of any kind
|
||||
- Running 3rd party container images on your system could open a
|
||||
security hole/attack vector/attack surface
|
||||
- An expert with knowledge how to build exploits will be able to
|
||||
jailbreak/elevate to root even if best practices are followed
|
||||
|
||||
We can also set up a port forward to a little web server:
|
||||
This being said, a system suspected of being compromised can always be
|
||||
restored to a safe state with a factory reset. Provided, of course,
|
||||
that it has secure boot enabled.
|
||||
|
||||
podman run -d --rm -p 80:80 docker://nginx:alpine
|
||||
|
||||
In detached (`-d`) state you can check the status using `podman ps` and
|
||||
try to connect to the web server:
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
curl http://localhost
|
||||
In the CLI, containers can be run in one of two ways:
|
||||
|
||||
1. `container run IMAGE [COMMAND]`, and
|
||||
2. enter `configure` context, then `edit container NAME`
|
||||
|
||||
The first is useful mostly for testing, or running single commands in an
|
||||
image. It is a wrapper for `podman run -it --rm ...`, while the latter
|
||||
is a wrapper and adaptation of `podman create ...`.
|
||||
|
||||
The second create a container with a semi-persistent writable layer that
|
||||
survives container restarts and host system restarts. However, if you
|
||||
change the container configuration or upgrade the image (see below), the
|
||||
container will be recreated and the writable layer is lost. This is why
|
||||
it is recommended to set up a named volume for directories, or use file
|
||||
[Content Mounts][], in your container you want truly persistent content.
|
||||
|
||||
In fact, in many cases the best way is to create a `read-only` container
|
||||
and use file mounts and volumes only for the critical parts. Podman
|
||||
ensures (using tmpfs) `read-only` containers still have writable
|
||||
directories for certain critical file system paths: `/dev`, `/dev/shm`,
|
||||
`/run`, `/tmp`, and `/var/tmp`. Meaning, what you most often need is
|
||||
writable volumes for `/var/lib` and `/etc`, or only file mounts for a
|
||||
few files in `/etc`. The actual needs depend on the container image and
|
||||
application to run.
|
||||
|
||||
> **Note:** when running containers from public registries, double-check
|
||||
> that they support the CPU architecture of your host system. Remember,
|
||||
> unlike virtualization, containers reuse the host's CPU and kernel.
|
||||
|
||||
|
||||
### Examples
|
||||
|
||||
Classic Hello World:
|
||||
|
||||
admin@example-c0-ff-ee:/> container run docker://hello-world
|
||||
|
||||
Persistent web server using nginx, sharing the host's network:
|
||||
|
||||
admin@example-c0-ff-ee:/> configure
|
||||
admin@example-c0-ff-ee:/config> edit container web
|
||||
admin@example-c0-ff-ee:/config/container/web> set image docker://nginx:alpine
|
||||
admin@example-c0-ff-ee:/config/container/web> set publish 80:80
|
||||
admin@example-c0-ff-ee:/config/container/web> set network host
|
||||
admin@example-c0-ff-ee:/config/container/web> leave
|
||||
admin@example-c0-ff-ee:/> show container
|
||||
|
||||
Exit to the shell and verify the service with curl, or try to attach
|
||||
to your device's IP address using your browser:
|
||||
|
||||
admin@example-c0-ff-ee:~$ curl http://localhost
|
||||
|
||||
or connect to port 80 of your running Infix system with a browser. See
|
||||
the following sections for how to add more interfaces and start/stop the
|
||||
container at boot/reboot.
|
||||
|
||||
> To add your own content to web server, place the HTML files in, e.g.,
|
||||
> `/cfg/www/*.html` and add `-v /cfg/www:/usr/share/nginx/html:ro` to
|
||||
> the command line (above). <https://hub.docker.com/_/nginx/>
|
||||
the following sections for how to add more interfaces and manage your
|
||||
container at runtime.
|
||||
|
||||
|
||||
### Multiple Networks
|
||||
Networking and Containers
|
||||
-------------------------
|
||||
|
||||
It is also possible to start a container with multiple networks. The
|
||||
approach shown here uses CNI profiles, which means the interfaces names
|
||||
inside the container will always be: `eth0`, `eth1`, etc.
|
||||
By default, unlike other systems, persistent[^1] containers have no
|
||||
networking enabled. All network access has to be set up explicitly.
|
||||
Currently two types of of container networks are supported:
|
||||
|
||||
A common setup is to use a VETH pair, with one end in the container and
|
||||
the other end routed, or bridged, to the rest of the world. The Infix
|
||||
[CLI Guide](cli/introduction.md) provides examples of both. In either
|
||||
case you need to create a matching CNI profile for one end of the VETH
|
||||
pair before starting the container, here we use two network profiles,
|
||||
the default podman bridge and the VETH profile:
|
||||
- `cni-host`: one end of a VETH pair, or a physical Ethernet port
|
||||
- `cni-bridge`: an IP masquerading bridge
|
||||
|
||||
cni create host net1 veth0a 192.168.0.42/24
|
||||
podman run -d --rm --net=podman,net1 --entrypoint "/linuxrc" \
|
||||
--privileged docker://troglobit/buildroot:latest
|
||||
|
||||
The first profile (`podman`) is a the default bridged profile. When a
|
||||
container is started with that (default behavior), podman dynamically
|
||||
creates a VETH pair which has one end attached as a bridge port in the
|
||||
`cni-podman0` bridge managed by podman, and the other end is brought up
|
||||
as `eth0` inside the container.
|
||||
### CNI Bridge
|
||||
|
||||
The second profile is the one we created, it uses the `host-device`
|
||||
profile and does not create anything, it simply lifts the peer end of
|
||||
the pair into the container as `eth1`. This CNI profile can also be
|
||||
used to hand over control of physical ports to a container.
|
||||
All interface configuration is done in configure context. Let's start
|
||||
by creating an IP masquerading bridge, a common default for containers:
|
||||
|
||||
> **Note:** here we start the container in `--privileged` mode. This
|
||||
> allows the container guest unfiltered access to the host system and it
|
||||
> might not be what you want for a production system. For that at least
|
||||
> SECCOMP is recommended, which is out of scope for this tutorial.
|
||||
admin@example-c0-ff-ee:/> configure
|
||||
admin@example-c0-ff-ee:/config> edit interface docker0
|
||||
admin@example-c0-ff-ee:/config/interface/docker0/> set type bridge
|
||||
admin@example-c0-ff-ee:/config/interface/docker0/> set container-network type cni-bridge
|
||||
admin@example-c0-ff-ee:/config/interface/docker0/> leave
|
||||
|
||||
### Real Example
|
||||
We have to declare the interface type, and then also declare it as a
|
||||
container network, ensuring the interface cannot be used by the system
|
||||
for any other purpose. E.g., a `cni-host` interface is supposed to be
|
||||
used by a container, by declaring it as such we can guarantee that it
|
||||
would never accidentally be added as a bridge or lag port. Hence, to
|
||||
move an interface currently set as a `bridge-port` it must be removed
|
||||
from the bridge before being given to a container.
|
||||
|
||||
To be able to preserve state in containers between reboots we need a
|
||||
writable layer, this is done with `podman create`, after which we can
|
||||
use `podman start`. We build on the previous example:
|
||||
The default subnet for a `cni-bridge` is 172.17.0.0/16, the bridge will
|
||||
take the `.1` address and hand out the rest of the range to containers
|
||||
in a round-robin like fashion. A container with this `network` get an
|
||||
automatically created VETH pair connection to the bridge and a lot of
|
||||
other networking parameters (DNS, default route) are set up.
|
||||
|
||||
cni create host net1 veth0a 192.168.0.42/24
|
||||
podman create --name system --conmon-pidfile=/run/pod:system.pid \
|
||||
--restart=no --systemd=false --tz=local --privileged \
|
||||
--net=podman,net1 --entrypoint "/linuxrc" -p 222:22 \
|
||||
docker://troglobit/buildroot:latest
|
||||
Some of the defaults of a `cni-bridge` can be changed, e.g., instead of
|
||||
`set container-network type cni-bridge`, above, do:
|
||||
|
||||
Here we map the host port 222 to the SSH port of the container, but one
|
||||
can just as easily map the host's port 22 (SSH). Just make sure to
|
||||
first disable the host's SSH service.
|
||||
admin@example-c0-ff-ee:/config/interface/docker0/> edit container-network
|
||||
admin@example-c0-ff-ee:/config/interface/docker0/container-network/> set type cni-bridge
|
||||
admin@example-c0-ff-ee:/config/interface/docker0/container-network/> edit subnet 192.168.0.0/16
|
||||
admin@example-c0-ff-ee:/config/interface/docker0/container-network/subnet/192.168.0.0/16/> set gateway 192.168.255.254
|
||||
admin@example-c0-ff-ee:/config/interface/docker0/container-network/subnet/192.168.0.0/16/> end
|
||||
admin@example-c0-ff-ee:/config/interface/docker0/container-network/> edit route 10.0.10.0/24
|
||||
admin@example-c0-ff-ee:/config/interface/docker0/container-network/route/10.0.10.0/24/> set gateway 192.168.10.254
|
||||
admin@example-c0-ff-ee:/config/interface/docker0/container-network/route/10.0.10.0/24/> end
|
||||
admin@example-c0-ff-ee:/config/interface/docker0/container-network/> end
|
||||
admin@example-c0-ff-ee:/config/interface/docker0/> leave
|
||||
|
||||
> **Note:** the new options used here are required for enabling
|
||||
> monitoring and automate start/stop of containers at boot/reboot.
|
||||
Other network settings, like DNS and domain, use built-in defaults in
|
||||
CNI, but can be overridden from each container. Other common settings
|
||||
per container is the IP address and name of the network interface inside
|
||||
the container. The default, after each stop/start cycle, or reboot of
|
||||
the host, is to name the interfaces `eth0`, `eth1`, in the order they
|
||||
are given in the `network` list, and to give the container the next
|
||||
address in a `cni-bridge`. Below an example of a system container calls
|
||||
`set network interface docker0`, here we show how to set options for
|
||||
that network:
|
||||
|
||||
This creates the named container `system` which we can now start:
|
||||
|
||||
podman start system
|
||||
|
||||
and stop:
|
||||
|
||||
podman stop system
|
||||
|
||||
For this particular image[^1] we need to modify its defaults a bit,
|
||||
because it is set up to run a DHCP client on the first Ethernet
|
||||
interface, which in our case is the `podman` default CNI bridge. In
|
||||
fact, we don't want the container to set up any networking since that is
|
||||
handled by Infix and podman.
|
||||
|
||||
root@infix-12-34-56:~$ podman start system
|
||||
root@infix-12-34-56:~$ podman exec -it system sh
|
||||
/ # rm -rf /etc/network/interfaces
|
||||
/ # exit
|
||||
root@infix-12-34-56:~$ podman stop system
|
||||
|
||||
The change is now saved in the writable layer and the next time the
|
||||
container is started it will look like this:
|
||||
|
||||
root@infix-12-34-56:~$ podman start system
|
||||
root@infix-12-34-56:~$ podman exec -it system sh
|
||||
/ # ifconfig
|
||||
eth0 Link encap:Ethernet HWaddr A2:32:4C:2B:5E:51
|
||||
inet addr:10.88.0.11 Bcast:10.88.255.255 Mask:255.255.0.0
|
||||
inet6 addr: fe80::a032:4cff:fe2b:5e51/64 Scope:Link
|
||||
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
||||
RX packets:204 errors:0 dropped:107 overruns:0 frame:0
|
||||
TX packets:15 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:0
|
||||
RX bytes:48240 (47.1 KiB) TX bytes:1102 (1.0 KiB)
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/> edit network docker0
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/network/docker0/>
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/network/docker0/> set option
|
||||
<string> Options for CNI bridges.
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/network/docker0/> help option
|
||||
NAME
|
||||
option <string>
|
||||
|
||||
eth1 Link encap:Ethernet HWaddr 56:B5:4E:D1:9C:E5
|
||||
inet addr:192.168.0.42 Bcast:192.168.0.255 Mask:255.255.255.0
|
||||
inet6 addr: fe80::54b5:4eff:fed1:9ce5/64 Scope:Link
|
||||
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
||||
RX packets:274 errors:0 dropped:213 overruns:0 frame:0
|
||||
TX packets:16 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:60926 (59.4 KiB) TX bytes:1377 (1.3 KiB)
|
||||
|
||||
Automating start/stop at boot/reboot is documented in the next section.
|
||||
|
||||
[^1]: A common task one has to do for many other standard images, e.g.,
|
||||
Alpine Linux and BusyBox available on Docker Hub.
|
||||
|
||||
### Hybrid Mode
|
||||
|
||||
Since container setup and configuration is not modeled in YANG yet, we
|
||||
use the Infix *Hybrid mode*, described in [Infix Variants](variant.md).
|
||||
|
||||
To start containers in *Hybrid Mode*, provided the images have been
|
||||
downloaded with `podman pull docker://troglobit/buildroot:latest` and
|
||||
a container created (above):
|
||||
|
||||
```
|
||||
root@infix:/cfg/start.d$ cat <<HERE >20-enable-container.sh
|
||||
#!/bin/sh
|
||||
# Remember to create the veth0a <--> vet0b pair in the CLI first!
|
||||
cni create host net1 veth0a 192.168.0.42/24
|
||||
cat <<EOF > /etc/finit.d/available/pod:system.conf
|
||||
service name:pod :system pid:!/run/pod:system.pid podman --syslog start system -- System container
|
||||
EOF
|
||||
initctl enable pod:system
|
||||
exit 0
|
||||
HERE
|
||||
root@infix:/cfg/start.d$ chmod +x 20-enable-container.sh
|
||||
```
|
||||
|
||||
Reboot to activate the changes. To activate the changes without
|
||||
rebooting, run the script and call `initctl reload`.
|
||||
|
||||
> **Note:** the `/etc` directory is a `tmpfs` ramdisk and contents will
|
||||
> be lost on reboot, so to retain custom CNI profiles after reboot you
|
||||
> need to either save them and restore in the script above, or recreate
|
||||
> them on every boot.
|
||||
DESCRIPTION
|
||||
Options for CNI bridges.
|
||||
Example: ip=1.2.3.4 to request a specific IP, both IPv4 and IPv6.
|
||||
interface_name=foo0 name to set interface name inside container.
|
||||
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/network/docker0/> set option ip=172.17.0.2
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/network/docker0/> set option interface_name=wan
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/network/docker0/> leave
|
||||
|
||||
|
||||
Enabling Container Support
|
||||
--------------------------
|
||||
### CNI Host
|
||||
|
||||
Container support is not enabled by default because it is not a common
|
||||
customer feature, it also prolongs build times a lot due to bringing in
|
||||
a build-time dependency on Go.
|
||||
Another common use-case is to move a network interface into the network
|
||||
namespace of a container. Which the CNI bridge network does behind the
|
||||
scenes with one end of the automatically created VETH pair. This works
|
||||
with regular Ethernet interfaces as well, but here we will use a VETH
|
||||
pair as an example along with a regular bridge (where other Ethernet
|
||||
interfaces may live as well).
|
||||
|
||||
However, customer specific builds may have it, and you can also roll
|
||||
your own based on any of the available `defconfigs`. For example:
|
||||
admin@example-c0-ff-ee:/config/> edit interface veth0
|
||||
admin@example-c0-ff-ee:/config/interface/veth0/> set veth peer ntpd
|
||||
admin@example-c0-ff-ee:/config/interface/veth0/> set ipv4 address 192.168.0.1 prefix-length 24
|
||||
admin@example-c0-ff-ee:/config/interface/veth0/> end
|
||||
admin@example-c0-ff-ee:/config/> edit interface ntpd
|
||||
admin@example-c0-ff-ee:/config/interface/ntpd/> set ipv4 address 192.168.0.2 prefix-length 24
|
||||
admin@example-c0-ff-ee:/config/interface/ntpd/> set container-network
|
||||
|
||||
cd infix/
|
||||
make x86_64_defconfig
|
||||
|
||||
Run menuconfig, search for `podman` using `/`, enable it and build:
|
||||
|
||||
make menuconfig
|
||||
...
|
||||
make
|
||||
|
||||
Enabling [podman][] select `crun`, `conmon`, and all other dependencies.
|
||||
The build will take a while, but eventually you can:
|
||||
|
||||
make run
|
||||
This is a routed setup, where we reserve 192.168.0.0/24 for the network
|
||||
between the host and the `ntpd` container. A perhaps more common case
|
||||
is to put `veth0` as a port in a bridge with other physical ports. The
|
||||
point of the routed case is that port forwarding from the container in
|
||||
this case is limited to a single interface, not *all interfaces* as is
|
||||
the default in the CNI Bridge setup.
|
||||
|
||||
|
||||
Debugging Containers
|
||||
--------------------
|
||||
### Host Networking
|
||||
|
||||
If the host system is not powered down or rebooted properly, containers
|
||||
may not start up as they should on the following boot. Below is a very
|
||||
common problem and solution shown.
|
||||
The third use-case is host networking, this is where a container share
|
||||
the network namespace of the host. An example here could be a nftables
|
||||
or ntpd container -- single applications which add core functionality to
|
||||
the host operating system.
|
||||
|
||||
```
|
||||
root@infix-12-34-56:~$ podman ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
root@infix-12-34-56:~$ grep nginx /var/log/syslog
|
||||
Jun 25 10:15:48 infix-12-34-56 finit[1]: Service pod:nginx[2376] died, restarting in 5000 msec (10/10)
|
||||
Jun 25 10:15:48 infix-12-34-56 finit[1]: Starting pod:nginx[2408]
|
||||
Jun 25 10:15:53 infix-12-34-56 finit[1]: Service pod:nginx keeps crashing, not restarting.
|
||||
```
|
||||
The host networking setup cannot be combined with any other network.
|
||||
|
||||
If this the system is isolated from remote network access, start by
|
||||
verifying the image is downloaded:
|
||||
|
||||
```
|
||||
root@infix-12-34-56:/cfg/start.d$ podman images
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
docker.io/library/nginx alpine 4937520ae206 10 days ago 43.2 MB
|
||||
docker.io/troglobit/buildroot latest 68faf6b20f1a 6 weeks ago 41.4 MB
|
||||
```
|
||||
|
||||
OK, let's see what the `podman-service` step (above) created:
|
||||
|
||||
```
|
||||
root@infix-12-34-56:/cfg/start.d$ initctl show pod-nginx.conf
|
||||
service name:pod :nginx podman run --name nginx --rm -p 80:80 nginx:alpine -- Nginx container
|
||||
```
|
||||
|
||||
Try starting the container manually. Remember to add the `-d` flag to
|
||||
emulate detached/background operation:
|
||||
|
||||
```
|
||||
root@infix-12-34-56:/cfg/start.d$ podman run --name nginx --rm -d -p 8080:80 nginx:alpine
|
||||
Error: creating container storage: the container name "nginx" is already in use by 9c73bd8d505b1585d241595bfadede361b87f6c1be9a5656253b5a4d73da57e0. You have to remove that container to be able to reuse that name: that name is already in use
|
||||
```
|
||||
|
||||
Aha, a lingering image with the same name! Where is it?
|
||||
|
||||
```
|
||||
root@infix-12-34-56:/cfg/start.d$ podman ps --all
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
f3386ae9517f docker.io/troglobit/buildroot:latest About an hour ago Exited (0) About an hour ago ecstatic_panini
|
||||
bf0c6178ea26 docker.io/troglobit/buildroot:latest About an hour ago Exited (0) About an hour ago determined_brown
|
||||
385155f479c0 docker.io/troglobit/buildroot:latest About an hour ago Exited (0) About an hour ago vibrant_engelbart
|
||||
99a1b3319d9e docker.io/troglobit/buildroot:latest About an hour ago Exited (0) About an hour ago dreamy_tesla
|
||||
9c73bd8d505b docker.io/library/nginx:alpine nginx -g daemon o... 11 minutes ago Created 0.0.0.0:80->80/tcp nginx
|
||||
8a5290504ebc docker.io/troglobit/buildroot:latest 10 minutes ago Created mystifying_liskov
|
||||
```
|
||||
|
||||
Oh, we have two lingering containers that were created but did not stop
|
||||
correctly. Let's remove them:
|
||||
|
||||
```
|
||||
root@infix-12-34-56:/cfg/start.d$ docker rm -f 9c73bd8d505b
|
||||
9c73bd8d505b
|
||||
root@infix-12-34-56:/cfg/start.d$ docker rm -f 8a5290504ebc
|
||||
8a5290504ebc
|
||||
```
|
||||
|
||||
Now we can manually restart the (supervised) container:
|
||||
|
||||
```
|
||||
root@infix-12-34-56:/cfg/start.d$ initctl restart pod:nginx
|
||||
root@infix-12-34-56:/cfg/start.d$ initctl status pod:nginx
|
||||
Status : running
|
||||
Identity : pod:nginx
|
||||
Description : Nginx container
|
||||
Origin : /etc/finit.d/enabled/pod-nginx.conf
|
||||
Command : podman run --name nginx --rm -p 80:80 nginx:alpine
|
||||
PID file : none
|
||||
PID : 2669
|
||||
User : root
|
||||
Group : root
|
||||
Uptime : 15 sec
|
||||
Restarts : 11 (0/10)
|
||||
Runlevels : [---234-----]
|
||||
Memory : 63.8M
|
||||
CGroup : /system/pod-nginx cpu 0 [100, max] mem [0, max]
|
||||
├─ 2669 podman run --name nginx --rm -p 80:80 nginx:alpine
|
||||
└─ 2816 conmon --api-version 1 -c 44d24aa7e98b67ff811596984462b902af3b09a04b4f9bef86e11d246b8cc2ff -u 44d24aa7e98b67ff8
|
||||
|
||||
Jun 25 10:15:48 infix-12-34-56 finit[1]: Service pod:nginx[2376] died, restarting in 5000 msec (10/10)
|
||||
Jun 25 10:15:48 infix-12-34-56 finit[1]: Starting pod:nginx[2408]
|
||||
Jun 25 10:15:53 infix-12-34-56 finit[1]: Service pod:nginx keeps crashing, not restarting.
|
||||
Jun 25 10:47:55 infix-12-34-56 finit[1]: Starting pod:nginx[2669]
|
||||
```
|
||||
For an example, see below.
|
||||
|
||||
|
||||
Mounts and Volumes
|
||||
------------------
|
||||
|
||||
It is possible to mount files, directories, and even files matching a
|
||||
glob, into a container. This gives precise control over the container's
|
||||
file system:
|
||||
|
||||
admin@example-c0-ff-ee:/config/container/system/> edit mount leds
|
||||
admin@example-c0-ff-ee:/config/container/system/mount/leds> set source /sys/class/leds
|
||||
admin@example-c0-ff-ee:/config/container/system/mount/leds> set target /sys/class/leds
|
||||
admin@example-c0-ff-ee:/config/container/system/mount/leds> end
|
||||
admin@example-c0-ff-ee:/config/container/system/>
|
||||
|
||||
Sometimes *volumes* are a better fit. A volume is an automatically
|
||||
created read-writable entity that follows the life of your container.
|
||||
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/> set volume varlib target /var/lib
|
||||
|
||||
Volumes survive reboots and upgrading of the base image, unlike the
|
||||
persistent writable layer you get by default, which does not survive
|
||||
upgrades. The volume is created by podman when the container first
|
||||
starts up, unlike a regular bind mount it synchronizes with the contents
|
||||
of the underlying container image's path on the first start. I.e.,
|
||||
"bind-mount, if empty: then rsync".
|
||||
|
||||
> Infix support named volumes (only), and it is not possible to share a
|
||||
> volume between containers. All the tricks possible with volumes may
|
||||
> be added in a later release.
|
||||
|
||||
### Content Mounts
|
||||
|
||||
Content mount is a special type of where the file contents for the
|
||||
container is stored alongside the container configuration. This can be
|
||||
very useful when deploying similar systems at multiple sites. When the
|
||||
host loads its `startup-config` (or even `factory-config`) a temporary
|
||||
file is created using the decoded base64 data from the `content` node.
|
||||
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/> edit mount ntpd.conf
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/mount/ntpd.conf> set content
|
||||
... interactive editor starts up ...
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/mount/ntpd.conf> set target /etc/ntpd.conf
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/mount/ntpd.conf> end
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/>
|
||||
|
||||
The editor is a small [Emacs clone called Mg][2], see the built-in help
|
||||
text, or press Ctrl-x Ctrl-c to exit and save. When the editor exits
|
||||
the contents are base64 encoded and stored in the candidate datastore.
|
||||
|
||||
> **Note:** since these files are always recreated when the host is
|
||||
> restarted, changes made by the container are not preserved, or saved
|
||||
> back to the host's startup-config even if the read-only option is off.
|
||||
|
||||
|
||||
Example Containers
|
||||
------------------
|
||||
|
||||
### System Container
|
||||
|
||||
Let's try out what we've learned by setting up a system container, a
|
||||
container providing multiple services, using the `docker0` interface
|
||||
we created previously:
|
||||
|
||||
admin@example-c0-ff-ee:/> configure
|
||||
admin@example-c0-ff-ee:/config> edit container system
|
||||
admin@example-c0-ff-ee:/config/container/system/> set image ghcr.io/kernelkit/curios:edge
|
||||
admin@example-c0-ff-ee:/config/container/system/> set network interface docker0
|
||||
admin@example-c0-ff-ee:/config/container/system/> set publish 222:22
|
||||
admin@example-c0-ff-ee:/config/container/system/> leave
|
||||
|
||||
> **Note:** ensure you have a network connection to the registry.
|
||||
> If the image cannot be pulled, creation of the container will be
|
||||
> put in a queue and be retried every time there is a change in the
|
||||
> routing table, e.g., default route is added.
|
||||
|
||||
Provided the image is downloaded successfully, a new `system` container
|
||||
now runs behind the docker0 interface, forwarding container port 22 to
|
||||
port 222 on all of the host's interfaces. (See `help publish` in the
|
||||
container configuration context for the full syntax.)
|
||||
|
||||
Available containers can be accessed from admin-exec:
|
||||
|
||||
admin@example-c0-ff-ee:/> show container
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
439af2917b44 ghcr.io/kernelkit/curios:edge 41 hours ago Up 16 hours 0.0.0.0:222->222/tcp system
|
||||
|
||||
This is a system container, so you can "attach" to it by starting a
|
||||
shell (or logging in with SSH):
|
||||
|
||||
admin@example-c0-ff-ee:/> container shell system
|
||||
root@439af2917b44:/#
|
||||
|
||||
Notice how the hostname inside the container changes. By default the
|
||||
container ID (hash) is used, but this can be easily changed:
|
||||
|
||||
root@439af2917b44:/# exit
|
||||
admin@infix-00-00-00:/> configure
|
||||
admin@infix-00-00-00:/config/> edit container system
|
||||
admin@infix-00-00-00:/config/container/system/> set hostname sys101
|
||||
admin@infix-00-00-00:/config/container/system/> leave
|
||||
admin@infix-00-00-00:/> container shell system
|
||||
root@sys101:/#
|
||||
|
||||
[^1]: this does not apply to the admin-exec command `container run`.
|
||||
This command is intended to be used for testing and evaluating
|
||||
container images. Such containers are given a private network
|
||||
behind an IP masquerading bridge.
|
||||
|
||||
|
||||
### Application Container: nftables
|
||||
|
||||
Infix currently does not have a native firewall configuration, and even
|
||||
when it does it will never expose the full capabilities of `nftables`.
|
||||
For really advanced setups, the following will be the only alternative:
|
||||
|
||||
admin@example-c0-ff-ee:/> configure
|
||||
admin@example-c0-ff-ee:/config> edit container nftables
|
||||
admin@example-c0-ff-ee:/config/container/system/> set image ghcr.io/kernelkit/curios-nftables:edge
|
||||
admin@example-c0-ff-ee:/config/container/system/> set network host
|
||||
admin@example-c0-ff-ee:/config/container/system/> edit mount nftables.conf
|
||||
admin@example-c0-ff-ee:/config/container/system/mount/nftables.conf/> set target /etc/nftables.conf
|
||||
admin@example-c0-ff-ee:/config/container/system/mount/nftables.conf/> set content
|
||||
... interactive editor starts up where you can paste your rules ...
|
||||
admin@example-c0-ff-ee:/config/container/system/mount/nftables.conf/> leave
|
||||
|
||||
|
||||
### Application Container: ntpd
|
||||
|
||||
The default NTP server/client in Infix is Chrony, a fully working and
|
||||
capable workhorse for most use-cases. However, it does not support a
|
||||
feature like multicasting, for that you need ISC ntpd.
|
||||
|
||||
As we did with `nftables`, previously, we can use host networking and
|
||||
set up a read-only config file that is bind-mounted into the container's
|
||||
file system and store in the host's `startup-config`. However, `ntpd`
|
||||
also saves clock drift information in `/var/lib/ntpd`, so we will also
|
||||
use volumes in this example.
|
||||
|
||||
admin@example-c0-ff-ee:/> configure
|
||||
admin@example-c0-ff-ee:/config> edit container ntpd
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/> set image ghcr.io/kernelkit/curios-ntpd:edge
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/> set network interface ntpd # From veth0 above
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/> edit mount ntp.conf
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/mount/ntp.conf/> set target /etc/ntp.conf
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/mount/ntp.conf/> set content
|
||||
... interactive editor starts up where you can paste your rules ...
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/mount/ntp.conf/> end
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/> edit volume varlib
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/volume/varlib/> set target /var/lib
|
||||
admin@example-c0-ff-ee:/config/container/ntpd/volume/varlib/> leave
|
||||
admin@example-c0-ff-ee:/> copy running-config startup-config
|
||||
|
||||
The `ntp.conf` file is stored in the host's `startup-config` and any
|
||||
state data in the container's `/var/lib` is retained between reboots
|
||||
and across image upgrades.
|
||||
|
||||
|
||||
Upgrading a Container Image
|
||||
---------------------------
|
||||
|
||||
All container configurations are locked to the image hash at the time of
|
||||
first download, not just ones that use an `:edge` or `:latest` tag. An
|
||||
upgrade of containers using versioned images is more obvious -- update
|
||||
the configuration -- but the latter is a bit trickier. Either remove
|
||||
the configuration and recreate it (leave/apply the changes between), or
|
||||
use the admin-exec level command:
|
||||
|
||||
admin@example-c0-ff-ee:/> container upgrade NAME
|
||||
|
||||
Where `NAME` is the name of your container. This command stops your
|
||||
container, does a `container pull IMAGE`, and then recreates the
|
||||
container with the new image. Upgraded containers are not automatically
|
||||
restarted.
|
||||
|
||||
admin@example-c0-ff-ee:/> container start NAME
|
||||
|
||||
> **Note:** the default writable layer is lost when upgrading the image
|
||||
> Use named volumes for directories with writable content you wish to
|
||||
> keep over an upgrade.
|
||||
|
||||
|
||||
[1]: https://github.com/kernelkit/infix/blob/main/src/confd/yang/infix-containers%402023-12-14.yang
|
||||
[2]: https://github.com/troglobit/mg
|
||||
[podman]: https://podman.io
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# Hardware information and status
|
||||
|
||||
The hardware infomation and status is handled by the YANG model [IETF hardware][ietf-hardware].
|
||||
with deviations and augmentations in _infix-hardware_.
|
||||
|
||||
## USB Ports
|
||||
For infix to be able to control the USB port(s), a device tree modification is needed (see _alder.dtsi_ for full example).
|
||||
```
|
||||
chosen {
|
||||
infix {
|
||||
usb-ports = <&cp0_usb3_1>;
|
||||
usb-port-names = "USB";
|
||||
};
|
||||
};
|
||||
```
|
||||
Two USB ports are also exposed in QEMU for test purpose.
|
||||
|
||||
All USB ports in the system will be disabled during boot due to the file _board/common/rootfs/etc/modprobe.d/usbcore.conf_.
|
||||
If you not want Infix to controll the USB port(s), remove the file or manually enable the USB bus, here is an example:
|
||||
|
||||
```
|
||||
# Enable the bus
|
||||
echo 1 > /sys/bus/usb/devices/usb1/authorized
|
||||
```
|
||||
And then enable sub-devices (e.g. USB memory)
|
||||
```
|
||||
# Enable a device plugged into usb1
|
||||
echo 1 > /sys/bus/usb/devices/usb1/1-1/authorized
|
||||
```
|
||||
|
||||
### Current status
|
||||
```
|
||||
admin@example:/> show hardware
|
||||
USB PORTS
|
||||
NAME STATE
|
||||
USB unlocked
|
||||
```
|
||||
An USB port can be in two states _unlocked_ and _locked_. When a port is locked,
|
||||
all connected devices will get power, but never authorized by Linux to use.
|
||||
|
||||
### Configure USB port
|
||||
> **Note:** You can only configure an USB ports that exist in _show hardware_ in admin exec.
|
||||
|
||||
```
|
||||
root@example:/> configure
|
||||
root@example:/config/> set hardware component USB state admin-state unlocked
|
||||
root@example:/config/> leave
|
||||
root@example:/>
|
||||
```
|
||||
|
||||
[ietf-hardware]: https://www.rfc-editor.org/rfc/rfc8348.html
|
||||
+148
-12
@@ -11,11 +11,18 @@ traffic at various points.
|
||||
TL;DR
|
||||
-----
|
||||
|
||||
make x86_64_defconfig
|
||||
make
|
||||
make test-qeneth
|
||||
Build Infix and run the test suite on a set of virtual Infix nodes.
|
||||
|
||||
Runs the test suite on a set of virtual Infix nodes.
|
||||
$ make x86_64_defconfig
|
||||
$ make
|
||||
$ make test
|
||||
|
||||
To run a subset of tests, e.g., only the DHCP client tests:
|
||||
|
||||
$ make test INFIX_TESTS=case/infix_dhcp/all.yaml
|
||||
|
||||
> **Note:** see the below section [Quick Start Guide][] for how to
|
||||
> connect to the test system, debug and develop tests, and more.
|
||||
|
||||
|
||||
Tenets
|
||||
@@ -121,15 +128,17 @@ against an Infix image started from `make run`. When the instance is
|
||||
running, you can open a separate terminal and run `make test-run`, to
|
||||
run the subset of the test suite that can be mapped to it.
|
||||
|
||||
Both `test-qeneth` and `test-run` targets have a respective target
|
||||
with a `-sh` suffix. These can be used to start an interactive
|
||||
session in the reproducible environment, which is usually much easier
|
||||
to work with during a debugging session.
|
||||
Both `make test` and `make test-run` targets have a respective target
|
||||
with a `-sh` suffix. These can be used to start an interactive session
|
||||
in the reproducible environment, which is usually much easier to work
|
||||
with during a debugging session.
|
||||
|
||||
Inside of the reproducible environment, a wrapper for Qeneth is
|
||||
automatically created that will run it from the running network's
|
||||
directory. E.g., running a plain `qeneth status` inside a `make
|
||||
test-qeneth-sh` environment will show the expected status information.
|
||||
Inside the reproducible environment, a wrapper for Qeneth is created for
|
||||
the running network's directory. E.g., calling `qeneth status` inside a
|
||||
`make test-sh` environment show the expected status information.
|
||||
|
||||
> **Note:** for more information and help writing a test, see the below
|
||||
> [Quick Start Guide][].
|
||||
|
||||
|
||||
Physical and Logical Topologies
|
||||
@@ -274,6 +283,133 @@ it offline, enable STP on all DUTs, and then verify that the resulting
|
||||
spanning tree matches the expected one.
|
||||
|
||||
|
||||
Quick Start Guide
|
||||
-----------------
|
||||
|
||||
When developing a test, instead of blindly coding in Python and running
|
||||
`make test` over and over, start a test shell:
|
||||
|
||||
$ make test-sh
|
||||
Info: Generating topology
|
||||
Info: Generating node YAML
|
||||
Info: Generating executables
|
||||
Info: Launching dut1
|
||||
Info: Launching dut2
|
||||
Info: Launching dut3
|
||||
Info: Launching dut4
|
||||
11:42:52 infamy0:test #
|
||||
|
||||
It takes a little while to start up, but then we have a shell prompt
|
||||
inside the container running Infamy. It's a very limited environment,
|
||||
but it has enough to easily run single tests, connect to the virtual
|
||||
devices, and *step* your code. Let's run a test:
|
||||
|
||||
11:42:53 infamy0:test # ./case/infix_dhcp/dhcp_basic.py
|
||||
|
||||
### Connecting to Infamy
|
||||
|
||||
The test system runs in a Docker container, so to get a shell prompt in
|
||||
*another terminal* you need to connect to that container. Infamy comes
|
||||
with a helper script for this:
|
||||
|
||||
$ ./test/shell
|
||||
|
||||
By default it connect to the latest started Infamy instance. If you for
|
||||
some reason run multiple instances of Infamy the `shell` script takes an
|
||||
optional argument "system", which is the hostname of the container you
|
||||
want to connect to:
|
||||
|
||||
$ ./test/shell infamy2
|
||||
|
||||
### Connecting to a DUT
|
||||
|
||||
All DUTs in a virtual Infamy topology are emulated in Qemu instances
|
||||
managed by qeneth. If you want to watch what's happening on one of the
|
||||
target systems, e.g., tail a log file or run `tcpdump` during the test,
|
||||
there is another helper script in Infamy for this:
|
||||
|
||||
$ ./test/console 1
|
||||
Trying 127.0.0.1...
|
||||
Connected to 127.0.0.1.
|
||||
|
||||
Infix -- a Network Operating System v23.11.0-226-g0c144da (console)
|
||||
infix-00-00-00 login: admin
|
||||
Password:
|
||||
.-------.
|
||||
| . . | Infix -- a Network Operating System
|
||||
|-. v .-| https://kernelkit.github.io
|
||||
'-'---'-'
|
||||
|
||||
Run the command 'cli' for interactive OAM
|
||||
|
||||
admin@infix-00-00-00:~$
|
||||
|
||||
From here we can observe `dut1` freely while running tests.
|
||||
|
||||
> The `console` script uses `telnet` to connect to a port forwarded to
|
||||
> `localhost` by the Docker container. To exit Telnet, use Ctrl-] and
|
||||
> then 'q' followed by enter. This can be customized in `~/.telnetrc`
|
||||
|
||||
Like the `shell` script, `console` takes an optional "system" argument
|
||||
in case you run multiple instances of Infamy:
|
||||
|
||||
$ ./test/console 1 infamy2
|
||||
|
||||
You can also connect to the console of a DUT from within a `shell`:
|
||||
|
||||
$ ./test/shell
|
||||
11:42:54 infamy0:test # qeneth status
|
||||
11:42:54 infamy0:test # qeneth console dut1
|
||||
login: admin
|
||||
password: *****
|
||||
admin@infix-00-00-00:~$
|
||||
|
||||
> **Note:** disconnect from the qeneth console by using bringing up the
|
||||
> old Telnet "menu" using Ctrl-], compared to standard Telnet this is
|
||||
> the BusyBox version so you press 'e' + enter instead of 'q' to quit.
|
||||
|
||||
|
||||
### Debug a Test
|
||||
|
||||
First, add a Python `breakpoint()` to your test and run it from your
|
||||
`make test-sh` Infamy instance:
|
||||
|
||||
11:42:58 infamy0:test # ./case/infix_dhcp/dhcp_basic.py
|
||||
# Starting (2024-02-10 11:42:59)
|
||||
# Probing dut1 on port d1a for IPv6LL mgmt address ...
|
||||
# Connecting to mgmt IP fe80::ff:fe00:0%d1a:830 ...
|
||||
ok 1 - Initialize
|
||||
> /home/jocke/src/infix/test/case/infix_dhcp/dhcp_basic.py(44)<module>()
|
||||
(Pdb)
|
||||
|
||||
You are now in the Python debugger, Pdb.
|
||||
|
||||
### Debug a Test in a MacVLAN
|
||||
|
||||
Most tests use some sort of network connection to the DUTs. From a test
|
||||
shell you cannot see or debug that connection by default. So you need
|
||||
to employ a little trick.
|
||||
|
||||
Open another terminal, and start a test shell in your Infamy instance:
|
||||
|
||||
$ ./test/shell
|
||||
11:43:19 infamy0:test #
|
||||
|
||||
MacVLAN interfaces created by Infamy run in another network namespace,
|
||||
to enter it we can look for a sleeper process:
|
||||
|
||||
11:43:20 infamy0:test # nsenter -n U -t $(pidof sleep infinity)
|
||||
|
||||
You are now one step further down:
|
||||
|
||||
infamy0:/home/jocke/src/infix/test# ip -br a
|
||||
lo UNKNOWN 127.0.0.1/8 ::1/128
|
||||
iface@if7 UP 10.0.0.1/24 fe80::38d0:88ff:fe77:b7cd/64
|
||||
|
||||
You can now freely debug the network activity of your test and the
|
||||
responses from the DUT.
|
||||
|
||||
|
||||
[9PM]: https://github.com/rical/9pm
|
||||
[Qeneth]: https://github.com/wkz/qeneth
|
||||
[TAP]: https://testanything.org/
|
||||
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
Vital Product Data
|
||||
==================
|
||||
|
||||
Infix can source Vital Product Data (VPD) from EEPROMs containing an
|
||||
[ONIE TLV][oniespec]. The load bearing information from VPDs, used by
|
||||
Infix by default, is:
|
||||
|
||||
1. Base (chassis) MAC address of the system
|
||||
2. Factory default admin password hash
|
||||
|
||||
Additionally, every VPD is listed as a separate _component_ in the
|
||||
_ietf-hardware_ model, in which Infix exports all available attributes
|
||||
to the user. This is useful to read out exact hardware revisions of
|
||||
each board making up a system, figure out if a system belongs to a
|
||||
particular production batch, etc.
|
||||
|
||||
|
||||
## JSON Encoding
|
||||
|
||||
To make EEPROM binary generation less cumbersome, Infix defines a JSON
|
||||
encoding of an ONIE TLV, and provides a tool, [onieprom], to translate
|
||||
from one encoding to another. I.e. a JSON object can be translated to
|
||||
its binary equivalent, and vice versa.
|
||||
|
||||
A mapping between TLV attribute IDs and JSON keys is defined, using
|
||||
the Linux kernel [driver] as a reference:
|
||||
|
||||
| TLV ID | Description | Key | Type |
|
||||
|--------|------------------|----------------------|---------------------------------|
|
||||
| `0x21` | Product Name | `"product-name"` | String |
|
||||
| `0x22` | Part Number | `"part-number"` | String |
|
||||
| `0x23` | Serial Number | `"serial-number"` | String |
|
||||
| `0x24` | MAC #1 Base | `"mac-address"` | String ("c0:ff:ee:00:00:00") |
|
||||
| `0x25` | Manufacture Date | `"manufacture-date"` | String ("MM/DD/YYYY HH:NN:SS") |
|
||||
| `0x26` | Device Version | `"device-version"` | Integer (u8) |
|
||||
| `0x27` | Label Revision | `"label-revision"` | String |
|
||||
| `0x28` | Platform Name | `"platform-name"` | String |
|
||||
| `0x29` | ONIE Version | `"onie-version"` | String |
|
||||
| `0x2a` | Num MACs | `"num-macs"` | Integer (u16) |
|
||||
| `0x2b` | Manufacturer | `"manufacturer"` | String |
|
||||
| `0x2c` | Country Code | `"country-code"` | String (ISO 3166-1 2-byte code) |
|
||||
| `0x2d` | Vendor | `"vendor"` | String |
|
||||
| `0x2e` | Diag Version | `"diag-version"` | String |
|
||||
| `0x2f` | Service Tag | `"service-tag"` | String |
|
||||
| `0xfd` | Vendor Extension | `"vendor-extension"` | List of extensions (see below) |
|
||||
|
||||
The JSON encoding of a TLV is a single JSON object with an
|
||||
arbitrary subset of the attributes from the list above.
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"product-name": "Wacky Widget",
|
||||
"serial-number": "#1",
|
||||
"manufacture-date": "02/13/2024 11:29:52"
|
||||
}
|
||||
```
|
||||
|
||||
### Vendor Extensions
|
||||
|
||||
As the specification explicitly mentions the option of supplying
|
||||
multiple vendor extensions, possibly multiple ones of the same type,
|
||||
the JSON chosen encoding is a list, where each element is itself a
|
||||
list of exactly two elements:
|
||||
|
||||
| Element # | Type |
|
||||
|-----------|-----------------------------------------------|
|
||||
| 1 | Integer (u32, [IANA enterprise number][pens]) |
|
||||
| 2 | String (UTF-8 encoded extension data) |
|
||||
|
||||
The format of the extension data is defined by the entity indicated by
|
||||
the IANA enterprise number. Restricting extension data to UTF-8
|
||||
encoded strings is a decision imposed by Infix; the ONIE specification
|
||||
allows for arbitrary binary data.
|
||||
|
||||
**Example**:
|
||||
```json
|
||||
{
|
||||
...
|
||||
"vendor-extension": [
|
||||
[ 12345, "my extension data" ]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Infix Specific Extensions
|
||||
|
||||
[Kernelkit][kkit]'s IANA enterprise number is `61046`, under which any
|
||||
extensions required by Infix are stored. The **only** valid extension
|
||||
data that may be stored under this number is documented in _this
|
||||
section_. If other device specific data needs to be stored in a VPD,
|
||||
you must associate that with an enterprise number under our control.
|
||||
|
||||
Every Kernelkit extension must be a valid JSON object containing an
|
||||
arbitrary subset of the following attributes:
|
||||
|
||||
| Key | Value | Description |
|
||||
|------------|----------------------------------|--------------------------------------|
|
||||
| `"pwhash"` | String (output of `mkpasswd(1)`) | Factory default password for `admin` |
|
||||
|
||||
Since the extension is itself stored in a JSON document, it has to be
|
||||
appropriately quoted.
|
||||
|
||||
**Example**:
|
||||
```json
|
||||
{
|
||||
...
|
||||
"vendor-extension": [
|
||||
[ 61046, "{\"pwhash\":\"$6$9rufAxdqCrxrwfQR$G0l9cTVlu/vOhxgo/uMKfRDOmZRd5XWF3vKr5da6qYoxuTJBS/Pl9K.5lrabWoWFFc.71yFMaSlZz0O8FtAtl.\"}" ]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Creating and Parsing ONIE EEPROM Binaries
|
||||
|
||||
```
|
||||
usage: onieprom [-h] [-e] [-d] [infile] [outfile]
|
||||
|
||||
positional arguments:
|
||||
infile
|
||||
outfile
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-e, --encode Encode JSON input to binary output
|
||||
-d, --decode Decode binary input to JSON output
|
||||
```
|
||||
|
||||
To convert a compatible JSON document (using the first example above)
|
||||
to its binary equivalent, we ask [onieprom] to _encode_ it for us:
|
||||
|
||||
```sh
|
||||
~$ onieprom -e example.json >example.bin
|
||||
~$ hexdump -C example.bin
|
||||
00000000 54 6c 76 49 6e 66 6f 00 01 00 2d 25 13 30 32 2f |TlvInfo...-%.02/|
|
||||
00000010 31 33 2f 32 30 32 34 20 31 31 3a 32 39 3a 35 32 |13/2024 11:29:52|
|
||||
00000020 21 0c 57 61 63 6b 79 20 57 69 64 67 65 74 23 02 |!.Wacky Widget#.|
|
||||
00000030 23 31 fe 04 dd 69 88 97 |#1...i..|
|
||||
00000038
|
||||
```
|
||||
|
||||
We can also run the process in reverse, to inspect the contents of a
|
||||
binary in its equivalent JSON representation:
|
||||
|
||||
```sh
|
||||
~$ onieprom -d example.bin >example-again.json
|
||||
~$ jq . example-again.json
|
||||
{
|
||||
"manufacture-date": "02/13/2024 11:29:52",
|
||||
"product-name": "Wacky Widget",
|
||||
"serial-number": "#1"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
[oniespec]: https://opencomputeproject.github.io/onie/design-spec/hw_requirements.html
|
||||
[onieprom]: ../board/common/rootfs/bin/onieprom
|
||||
[driver]: https://elixir.bootlin.com/linux/latest/source/drivers/nvmem/layouts/onie-tlv.c
|
||||
[pens]: https://www.iana.org/assignments/enterprise-numbers/
|
||||
[kkit]: https://github.com/kernelkit
|
||||
@@ -3,6 +3,7 @@ IXMSG = printf "\e[37;44m>>> $(call qstrip,$(1))\e[0m\n"
|
||||
include $(sort $(wildcard $(BR2_EXTERNAL_INFIX_PATH)/package/*/*.mk))
|
||||
include $(BR2_EXTERNAL_INFIX_PATH)/board/common/common.mk
|
||||
-include $(BR2_EXTERNAL_INFIX_PATH)/board/$(patsubst "%",%,$(BR2_ARCH))/board.mk
|
||||
include $(BR2_EXTERNAL_INFIX_PATH)/infix.mk
|
||||
|
||||
.PHONY: local.mk
|
||||
local.mk:
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
INFIX_CFLAGS:=-Wall -Werror -Wextra -Wno-unused-parameter -Wformat=2 -Wformat-overflow=2 -Winit-self -Wstrict-overflow=4 -Wno-format-truncation -Wno-format-nonliteral
|
||||
@@ -1,4 +1,6 @@
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/confd/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/curios-httpd/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/execd/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/statd/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/conmon/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/factory/Config.in"
|
||||
@@ -6,6 +8,7 @@ source "$BR2_EXTERNAL_INFIX_PATH/package/faux/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/finit/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/ifupdown-ng/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/iito/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/k8s-logger/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/keyack/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/klish-plugin-infix/Config.in"
|
||||
source "$BR2_EXTERNAL_INFIX_PATH/package/klish/Config.in"
|
||||
|
||||
@@ -8,7 +8,7 @@ run name:error :1 log:console norestart if:<run/bootstrap/failure> \
|
||||
[S] /usr/libexec/confd/error --
|
||||
|
||||
service name:confd log <run/bootstrap/success> \
|
||||
[S12345] sysrepo-plugind -f -p /run/confd.pid -n -v3 -- Configuration daemon
|
||||
[S12345] sysrepo-plugind -f -p /run/confd.pid -n -- Configuration daemon
|
||||
|
||||
# Bootstrap system with startup-config
|
||||
run name:startup log:prio:user.notice norestart <pid/confd> \
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#
|
||||
################################################################################
|
||||
|
||||
|
||||
CONFD_VERSION = 1.0
|
||||
CONFD_SITE_METHOD = local
|
||||
CONFD_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/confd
|
||||
@@ -12,6 +13,15 @@ CONFD_LICENSE_FILES = LICENSE
|
||||
CONFD_REDISTRIBUTE = NO
|
||||
CONFD_DEPENDENCIES = augeas jansson libite sysrepo libsrx
|
||||
CONFD_AUTORECONF = YES
|
||||
define CONFD_CONF_ENV
|
||||
CFLAGS="$(INFIX_CFLAGS)"
|
||||
endef
|
||||
|
||||
ifeq ($(BR2_PACKAGE_PODMAN),y)
|
||||
CONFD_CONF_OPTS += --enable-containers
|
||||
else
|
||||
CONFD_CONF_OPTS += --disable-containers
|
||||
endif
|
||||
|
||||
define CONFD_INSTALL_EXTRA
|
||||
cp $(CONFD_PKGDIR)/confd.conf $(FINIT_D)/available/
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
config BR2_PACKAGE_CURIOS_HTTPD
|
||||
bool "curiOS-httpd"
|
||||
help
|
||||
Bundle an OCI archive of curiOS-httpd in /lib/oci for
|
||||
the given target architecture.
|
||||
|
||||
https://github.com/kernelkit/curiOS
|
||||
@@ -0,0 +1,18 @@
|
||||
################################################################################
|
||||
#
|
||||
# curios-httpd
|
||||
#
|
||||
################################################################################
|
||||
|
||||
CURIOS_HTTPD_VERSION = edge
|
||||
CURIOS_HTTPD_SOURCE = curios-httpd-oci-$(GO_GOARCH).tar.gz
|
||||
CURIOS_HTTPD_SITE = https://github.com/kernelkit/curiOS/releases/download/$(CURIOS_HTTPD_VERSION)
|
||||
CURIOS_HTTPD_LICENSE = GPL
|
||||
CURIOS_HTTPD_INSTALL_TARGET = YES
|
||||
|
||||
define CURIOS_HTTPD_INSTALL_TARGET_CMDS
|
||||
mkdir -p $(TARGET_DIR)/lib/oci
|
||||
(cd $(@D)/.. && $(TAR) cfz $(TARGET_DIR)/lib/oci/$(notdir $(@D)).tar.gz $(notdir $(@D)))
|
||||
endef
|
||||
|
||||
$(eval $(generic-package))
|
||||
@@ -0,0 +1,7 @@
|
||||
config BR2_PACKAGE_EXECD
|
||||
bool "execd"
|
||||
select BR2_PACKAGE_LIBUEV
|
||||
select BR2_PACKAGE_LIBITE
|
||||
help
|
||||
Generic job queue executor with retry on route changes.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
service log:prio:local1.err,tag:container \
|
||||
[2345] execd /run/containers/queue /var/lib/containers/active -- Container job runner
|
||||
@@ -0,0 +1,29 @@
|
||||
################################################################################
|
||||
#
|
||||
# execd
|
||||
#
|
||||
################################################################################
|
||||
|
||||
EXECD_VERSION = 1.0
|
||||
EXECD_SITE_METHOD = local
|
||||
EXECD_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/execd
|
||||
EXECD_LICENSE = ISC
|
||||
EXECD_LICENSE_FILES = LICENSE
|
||||
EXECD_REDISTRIBUTE = NO
|
||||
EXECD_DEPENDENCIES = libuev libite
|
||||
EXECD_AUTORECONF = YES
|
||||
|
||||
define EXECD_CONF_ENV
|
||||
CFLAGS="$(INFIX_CFLAGS)"
|
||||
endef
|
||||
|
||||
EXECD_CONF_OPTS = --prefix= --disable-silent-rules
|
||||
|
||||
define EXECD_INSTALL_EXTRA
|
||||
cp $(EXECD_PKGDIR)/execd.conf $(FINIT_D)/available/
|
||||
ln -sf ../available/execd.conf $(FINIT_D)/enabled/execd.conf
|
||||
cp $(EXECD_PKGDIR)/tmpfiles.conf $(TARGET_DIR)/etc/tmpfiles.d/execd.conf
|
||||
endef
|
||||
EXECD_TARGET_FINALIZE_HOOKS += EXECD_INSTALL_EXTRA
|
||||
|
||||
$(eval $(autotools-package))
|
||||
@@ -0,0 +1,8 @@
|
||||
d /run/containers/args 0700 - -
|
||||
d /run/containers/files 0700 - -
|
||||
d /var/lib/containers/active 0700 - -
|
||||
d /var/lib/containers/oci 0755 - -
|
||||
d /run/containers/inbox 0700 - -
|
||||
d /run/containers/queue 0700 - -
|
||||
d /run/cni 0755 - -
|
||||
L+ /var/lib/cni - - - - /run/cni
|
||||
@@ -11,6 +11,10 @@ FACTORY_SITE_METHOD = local
|
||||
FACTORY_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/factory
|
||||
FACTORY_REDISTRIBUTE = NO
|
||||
|
||||
define FACTORY_CONF_ENV
|
||||
CFLAGS="$(INFIX_CFLAGS)"
|
||||
endef
|
||||
|
||||
define FACTORY_BUILD_CMDS
|
||||
$(TARGET_MAKE_ENV) $(TARGET_CONFIGURE_OPTS) $(MAKE) -C $(@D) \
|
||||
LDLIBS="$(TARGET_LDFLAGS)"
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
config BR2_PACKAGE_K8S_LOGGER
|
||||
bool "k8s-logger"
|
||||
depends on BR2_PACKAGE_PODMAN
|
||||
depends on BR2_PACKAGE_SYSKLOGD
|
||||
select BR2_PACKAGE_LIBITE
|
||||
help
|
||||
Reads k8s-file style logs written to a fifo (mkfifo) by by podman
|
||||
and forwards them, line by assembled line, to syslog.
|
||||
|
||||
Infix requires use of sysklogd to enable the syslogp() API so that
|
||||
k8s-logger also includes the container's main PID.
|
||||
|
||||
https://github.com/kernelkit/k8s-logger
|
||||
@@ -0,0 +1,3 @@
|
||||
# LICENSE hash locally calculated, .tar.gz hash from GitHub release
|
||||
sha256 01b0285cf2ed7aa2f4c4bc92db3bbe9dcfe75441eda17d93c08b3fe9e32d2111 k8s-logger-1.2.tar.gz
|
||||
sha256 c556e258d3293cb96277912daf43155d9d65194fb5651249d1fdc489265b9a69 LICENSE
|
||||
@@ -0,0 +1,14 @@
|
||||
################################################################################
|
||||
#
|
||||
# k8s-logger
|
||||
#
|
||||
################################################################################
|
||||
|
||||
K8S_LOGGER_VERSION = 1.2
|
||||
K8S_LOGGER_SITE = https://github.com/kernelkit/k8s-logger/releases/download/v$(K8S_LOGGER_VERSION)
|
||||
K8S_LOGGER_LICENSE = MIT
|
||||
K8S_LOGGER_LICENSE_FILES = LICENSE
|
||||
K8S_LOGGER_DEPENDENCIES = sysklogd libite
|
||||
K8S_LOGGER_CONF_OPTS = --with-syslogp
|
||||
|
||||
$(eval $(autotools-package))
|
||||
@@ -6,3 +6,13 @@ config BR2_PACKAGE_KLISH_PLUGIN_INFIX
|
||||
help
|
||||
A plugin to klish with an opinionated config and some
|
||||
command extensions, suitable for systems running Infix.
|
||||
|
||||
if BR2_PACKAGE_KLISH_PLUGIN_INFIX
|
||||
|
||||
config BR2_PACKAGE_KLISH_PLUGIN_INFIX_SHELL
|
||||
bool "shell command"
|
||||
default y
|
||||
help
|
||||
Include 'shell' command exit to Bash.
|
||||
|
||||
endif
|
||||
|
||||
@@ -13,6 +13,28 @@ KLISH_PLUGIN_INFIX_REDISTRIBUTE = NO
|
||||
KLISH_PLUGIN_INFIX_DEPENDENCIES = klish-plugin-sysrepo
|
||||
KLISH_PLUGIN_INFIX_AUTORECONF = YES
|
||||
|
||||
define KLISH_PLUGIN_INFIX_CONF_ENV
|
||||
CFLAGS="$(INFIX_CFLAGS)"
|
||||
endef
|
||||
|
||||
ifeq ($(BR2_PACKAGE_PODMAN),y)
|
||||
KLISH_PLUGIN_INFIX_CONF_OPTS += --enable-containers
|
||||
else
|
||||
KLISH_PLUGIN_INFIX_CONF_OPTS += --disable-containers
|
||||
endif
|
||||
|
||||
ifeq ($(BR2_PACKAGE_KLISH_PLUGIN_INFIX_SHELL),y)
|
||||
KLISH_PLUGIN_INFIX_CONF_OPTS += --enable-shell
|
||||
else
|
||||
KLISH_PLUGIN_INFIX_CONF_OPTS += --disable-shell
|
||||
endif
|
||||
|
||||
ifeq ($(BR2_PACKAGE_BASH),y)
|
||||
KLISH_PLUGIN_INFIX_CONF_OPTS += --with-shell=/bin/bash
|
||||
else
|
||||
KLISH_PLUGIN_INFIX_CONF_OPTS += --with-shell=/bin/sh
|
||||
endif
|
||||
|
||||
define KLISH_PLUGIN_INFIX_INSTALL_DOC
|
||||
$(INSTALL) -t $(TARGET_DIR)/usr/share/infix/cli -D -m 0644 \
|
||||
$(wildcard $(BR2_EXTERNAL_INFIX_PATH)/doc/cli/*.md)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Locally calculated
|
||||
sha256 9d9d33b873917ca5d0bdcc47a36d2fd385971ab0c045d1472fcadf95ee5bcf5b LICENCE
|
||||
sha256 59ce3461a002a8113be030ef476940027f5e0e73dbe8afc56739cda6b55349ab klish-plugin-sysrepo-f828a96efb12c3f7f8807aae089561fd69f4a6af-br1.tar.gz
|
||||
sha256 c06a369147ae1071c0c309c0bd9d49d8ae0ff5dd1e804e2009b1a18f7fc12b5c klish-plugin-sysrepo-909cbfb1253a3c3c3abfdc72de9db7aaf3a08de2-br1.tar.gz
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
################################################################################
|
||||
|
||||
KLISH_PLUGIN_SYSREPO_VERSION = f828a96efb12c3f7f8807aae089561fd69f4a6af
|
||||
KLISH_PLUGIN_SYSREPO_VERSION = 909cbfb1253a3c3c3abfdc72de9db7aaf3a08de2
|
||||
KLISH_PLUGIN_SYSREPO_SITE = https://github.com/kernelkit/klish-plugin-sysrepo.git
|
||||
#KLISH_PLUGIN_SYSREPO_VERSION = cdd3eb51a7f7ee0ed5bd925fa636061d3b1b85fb
|
||||
#KLISH_PLUGIN_SYSREPO_SITE = https://src.libcode.org/pkun/klish-plugin-sysrepo.git
|
||||
|
||||
@@ -6,6 +6,8 @@ UnixSocketPath=/run/klishd.sock
|
||||
Pager="/usr/bin/less -I -F -e -X -K -d -r -S"
|
||||
UsePager=y
|
||||
|
||||
Banner="\nSee the 'help' command for an introduction to the system\n\n"
|
||||
|
||||
# Set max size of history. By default 100 lines are saved, for every new
|
||||
# line after that, the oldest line is dropped. Set to 0 for unlimited.
|
||||
#HistorySize=100
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Locally calculated
|
||||
sha256 9d9d33b873917ca5d0bdcc47a36d2fd385971ab0c045d1472fcadf95ee5bcf5b LICENCE
|
||||
sha256 0ef97fe42377e32be96a69096fc149e547568e6567d641cc50252356eb64e934 klish-ce81f52b50bcc3c37ce1ce2bec5a7965e2d698d2-br1.tar.gz
|
||||
sha256 edb7b9a53d57d87756a99fa25c4af3d6219bed9a863541e672580a2d1f6e7052 klish-b898fdd9a128adaad3a5c92d8dd7baac2085df1b-br1.tar.gz
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
################################################################################
|
||||
|
||||
KLISH_VERSION = ce81f52b50bcc3c37ce1ce2bec5a7965e2d698d2
|
||||
KLISH_VERSION = b898fdd9a128adaad3a5c92d8dd7baac2085df1b
|
||||
KLISH_SITE = https://github.com/kernelkit/klish.git
|
||||
#KLISH_VERSION = tags/3.0.0
|
||||
#KLISH_SITE = https://src.libcode.org/pkun/klish.git
|
||||
|
||||
@@ -13,5 +13,7 @@ LIBSRX_INSTALL_STAGING = YES
|
||||
LIBSRX_REDISTRIBUTE = NO
|
||||
LIBSRX_DEPENDENCIES = sysrepo
|
||||
LIBSRX_AUTORECONF = YES
|
||||
|
||||
define LIBSRX_CONF_ENV
|
||||
CFLAGS="$(INFIX_CFLAGS)"
|
||||
endef
|
||||
$(eval $(autotools-package))
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Create CNI profiles for podman
|
||||
|
||||
dir=/etc/cni/net.d
|
||||
force=
|
||||
num=90
|
||||
|
||||
usage()
|
||||
{
|
||||
cat <<EOF
|
||||
usage:
|
||||
cni [opt] cmd
|
||||
|
||||
options:
|
||||
-f Force operation, e.g. overwrite
|
||||
-h Show this help text
|
||||
-n NUM Create $dir/NUM-name-type.conflist
|
||||
|
||||
commands:
|
||||
create host NAME IFNAME IPADDR/PREFIX Create CNI host-device profile
|
||||
help Show this help text
|
||||
list List available profiles
|
||||
show PROFILE Show profile from list command
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
create_host()
|
||||
{
|
||||
nm=$1
|
||||
iface=$2
|
||||
addr=$3
|
||||
fn="$dir/$num-$nm-host.conflist"
|
||||
|
||||
if [ -f "$fn" ] && [ "$force" = "" ]; then
|
||||
echo "$fn: already exists, use -f to force overwriting it."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cat <<EOF > "$fn"
|
||||
{
|
||||
"cniVersion": "0.4.0",
|
||||
"name": "$nm",
|
||||
"plugins": [
|
||||
{
|
||||
"type": "host-device",
|
||||
"device": "$iface",
|
||||
"ipam": {
|
||||
"type": "static",
|
||||
"addresses": [
|
||||
{
|
||||
"address": "$addr"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
case $1 in
|
||||
-f | --force)
|
||||
force=true
|
||||
;;
|
||||
|
||||
-h | --help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
|
||||
-n | --num)
|
||||
num=$2
|
||||
shift
|
||||
;;
|
||||
|
||||
*)
|
||||
break
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
cmd=$1
|
||||
if [ -n "$cmd" ]; then
|
||||
shift
|
||||
fi
|
||||
|
||||
case $cmd in
|
||||
c | create)
|
||||
type=$1
|
||||
shift
|
||||
case $type in
|
||||
host | veth)
|
||||
#shellcheck disable=SC2068
|
||||
create_host $@
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported type"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
l | ls | list)
|
||||
ls -l $dir
|
||||
;;
|
||||
|
||||
show | sh | cat)
|
||||
fn=$1
|
||||
[ -f "$fn" ] || fn="$dir/$1"
|
||||
jq . "$fn"
|
||||
;;
|
||||
|
||||
help | *)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
@@ -1,106 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Create a monitored container service
|
||||
|
||||
pmargs=""
|
||||
enable=
|
||||
dryrun=
|
||||
name=
|
||||
|
||||
usage()
|
||||
{
|
||||
cat <<EOF
|
||||
summary:
|
||||
Create a supervised Finit service for a podman container.
|
||||
|
||||
Use '-e' flag to also enable the service. When called at bootstrap,
|
||||
this ensures Finit starts the new service when changing runlevel.
|
||||
|
||||
Please note, either the entry point or the CMD must run in foreground.
|
||||
|
||||
usage:
|
||||
podman-service [opts] IMAGE [CMD [ARG]]
|
||||
|
||||
options:
|
||||
-d "DESCRIPTION" Optional service description, default: "NAME container"
|
||||
-e Enable service (does not reload Finit)
|
||||
-h Show this help text
|
||||
-n NAME Short name, lower-case, no spaces, e.g., 'nginx'
|
||||
-p "ARGS" Arguments for podman, example: '-p "-p 80:80"'
|
||||
-s Simulate (dry run), only echo to stdout
|
||||
|
||||
args:
|
||||
IMAGE Image or URL, e.g., docker://nginx:alpine
|
||||
CMD Optional command/daemon to run
|
||||
ARG Optional arguments to command, e.g., --foreground
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
service_generate()
|
||||
{
|
||||
image=$1
|
||||
cmd=$2
|
||||
arg=$3
|
||||
svc="pod-$name"
|
||||
gen="initctl -fb create $svc"
|
||||
|
||||
if [ -z "$description" ]; then
|
||||
description="$name container"
|
||||
fi
|
||||
# shellcheck disable=SC2209
|
||||
if [ -n "$dryrun" ]; then
|
||||
gen=cat
|
||||
enable=
|
||||
fi
|
||||
|
||||
cat <<EOF | $gen
|
||||
service name:pod :$name log podman run --name $name --rm $pmargs $image $cmd $arg -- $description
|
||||
EOF
|
||||
if [ -n "$enable" ]; then
|
||||
initctl -fb enable "pod-$name"
|
||||
fi
|
||||
}
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
case $1 in
|
||||
-d)
|
||||
shift
|
||||
description="$1"
|
||||
;;
|
||||
-e)
|
||||
enable=true
|
||||
;;
|
||||
-h|--help|help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
-n)
|
||||
shift
|
||||
name="$1"
|
||||
;;
|
||||
-p)
|
||||
shift
|
||||
pmargs="$pmargs $1"
|
||||
;;
|
||||
-s)
|
||||
dryrun=true
|
||||
;;
|
||||
*)
|
||||
break
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
image=$1
|
||||
if [ -z "$image" ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
|
||||
if [ -z "$name" ]; then
|
||||
name=$(echo "$image" | sed 's/\(.*\):.*/\1/g')
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2068
|
||||
service_generate "$image" $@
|
||||
@@ -71,6 +71,13 @@ else
|
||||
PODMAN_TAGS += exclude_graphdriver_vfs
|
||||
endif
|
||||
|
||||
ifeq ($(BR2_PACKAGE_BASH_COMPLETION),y)
|
||||
define PODMAN_BASH_COMPLETION
|
||||
$(INSTALL) -D -m 644 $(@D)/completions/bash/podman \
|
||||
$(TARGET_DIR)/usr/share/bash-completion/completions/
|
||||
endef
|
||||
endif
|
||||
|
||||
define PODMAN_INSTALL_TARGET_CMDS
|
||||
$(TARGET_MAKE_ENV) $(MAKE) -C $(@D) DESTDIR=$(TARGET_DIR) PREFIX=/usr \
|
||||
install.bin
|
||||
@@ -81,11 +88,8 @@ define PODMAN_INSTALL_TARGET_CMDS
|
||||
$(INSTALL) -d -m 755 $(TARGET_DIR)/etc/containers
|
||||
$(INSTALL) -D -m 644 $(PODMAN_PKGDIR)/containers-policy.json \
|
||||
$(TARGET_DIR)/etc/containers/policy.json
|
||||
$(INSTALL) -D -m 755 $(PODMAN_PKGDIR)/podman-service \
|
||||
$(TARGET_DIR)/usr/bin/podman-service
|
||||
$(INSTALL) -D -m 755 $(PODMAN_PKGDIR)/cni \
|
||||
$(TARGET_DIR)/usr/bin/cni
|
||||
$(PODMAN_SECCOMP_PROFILE)
|
||||
$(PODMAN_BASH_COMPLETION)
|
||||
endef
|
||||
|
||||
define PODMAN_INSTALL_INIT_SYSTEMD
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
# Use <pid/syslogd> as barrier for other system tasks and service that
|
||||
# rely on modules, firmware, and device nodes to be ready.
|
||||
service if:udevd env:-/etc/default/sysklogd <run/udevadm:5/success> \
|
||||
service if:udevd nowarn env:-/etc/default/sysklogd <run/udevadm:5/success> \
|
||||
[S0123456789] syslogd -F $SYSLOGD_ARGS -- System log daemon
|
||||
service if:mdev env:-/etc/default/sysklogd <run/coldplug/success> \
|
||||
service if:mdev nowarn env:-/etc/default/sysklogd <run/coldplug/success> \
|
||||
[S0123456789] syslogd -F $SYSLOGD_ARGS -- System log daemon
|
||||
service if:mdevd env:-/etc/default/sysklogd <run/coldplug/success> \
|
||||
service if:mdevd nowarn env:-/etc/default/sysklogd <run/coldplug/success> \
|
||||
[S0123456789] syslogd -F $SYSLOGD_ARGS -- System log daemon
|
||||
|
||||
@@ -4,6 +4,7 @@ config BR2_PACKAGE_STATD
|
||||
select BR2_PACKAGE_LIBEV
|
||||
select BR2_PACKAGE_SYSREPO
|
||||
select BR2_PACKAGE_LIBSRX
|
||||
select BR2_PACKAGE_LIBITE
|
||||
help
|
||||
Operational Status Daemon. Responsible for handling sysrepo
|
||||
operational run-time info. Such as interface state and address.
|
||||
|
||||
+11
-8
@@ -11,16 +11,19 @@ STATD_LICENSE = BSD-3-Clause
|
||||
STATD_LICENSE_FILES = LICENSE
|
||||
STATD_REDISTRIBUTE = NO
|
||||
STATD_DEPENDENCIES = sysrepo libev libsrx jansson
|
||||
STATD_AUTORECONF = YES
|
||||
|
||||
define STATD_BUILD_CMDS
|
||||
$(TARGET_MAKE_ENV) $(TARGET_CONFIGURE_OPTS) $(MAKE) -C $(@D) \
|
||||
LDFLAGS="$(TARGET_LDFLAGS)"
|
||||
define STATD_CONF_ENV
|
||||
CFLAGS="$(INFIX_CFLAGS)"
|
||||
endef
|
||||
|
||||
define STATD_INSTALL_TARGET_CMDS
|
||||
$(TARGET_MAKE_ENV) $(TARGET_CONFIGURE_OPTS) $(MAKE) -C $(@D) \
|
||||
DESTDIR="$(TARGET_DIR)" install
|
||||
endef
|
||||
STATD_CONF_OPTS = --prefix= --disable-silent-rules
|
||||
|
||||
ifeq ($(BR2_PACKAGE_PODMAN),y)
|
||||
STATD_CONF_OPTS += --enable-containers
|
||||
else
|
||||
STATD_CONF_OPTS += --disable-containers
|
||||
endif
|
||||
|
||||
define STATD_INSTALL_EXTRA
|
||||
cp $(STATD_PKGDIR)/statd.conf $(FINIT_D)/available/
|
||||
@@ -28,4 +31,4 @@ define STATD_INSTALL_EXTRA
|
||||
endef
|
||||
STATD_TARGET_FINALIZE_HOOKS += STATD_INSTALL_EXTRA
|
||||
|
||||
$(eval $(generic-package))
|
||||
$(eval $(autotools-package))
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
From cbf96a1de0feee85f8a4014f29f9f3b7558dbff0 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Sun, 10 Dec 2023 16:42:29 +0100
|
||||
Subject: [PATCH 1/2] Silence 'not available' log messages for nowarn
|
||||
run/task/service
|
||||
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
---
|
||||
src/service.c | 4 ++--
|
||||
src/svc.c | 5 +++--
|
||||
src/svc.h | 2 +-
|
||||
3 files changed, 6 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/src/service.c b/src/service.c
|
||||
index c6ee18d0..1ee54e5a 100644
|
||||
--- a/src/service.c
|
||||
+++ b/src/service.c
|
||||
@@ -1620,7 +1620,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
strlcat(ident, id, sizeof(ident));
|
||||
}
|
||||
|
||||
- if (ifstmt && !svc_ifthen(1, ident, ifstmt))
|
||||
+ if (ifstmt && !svc_ifthen(1, ident, ifstmt, nowarn))
|
||||
return 0;
|
||||
|
||||
levels = conf_parse_runlevels(runlevels);
|
||||
@@ -2027,7 +2027,7 @@ void service_mark_unavail(void)
|
||||
if (!svc->ifstmt[0])
|
||||
continue;
|
||||
|
||||
- if (!svc_ifthen(1, svc_ident(svc, buf, sizeof(buf)), svc->ifstmt))
|
||||
+ if (!svc_ifthen(1, svc_ident(svc, buf, sizeof(buf)), svc->ifstmt, svc->nowarn))
|
||||
svc_mark(svc);
|
||||
}
|
||||
}
|
||||
diff --git a/src/svc.c b/src/svc.c
|
||||
index 001993de..6bb90049 100644
|
||||
--- a/src/svc.c
|
||||
+++ b/src/svc.c
|
||||
@@ -649,7 +649,7 @@ int svc_enabled(svc_t *svc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
- if (!svc_ifthen(0, NULL, svc->ifstmt)) {
|
||||
+ if (!svc_ifthen(0, NULL, svc->ifstmt, svc->nowarn)) {
|
||||
// dbg("ifthen:%s>", svc->ifstmt[0] ? svc->ifstmt : "<");
|
||||
return 0;
|
||||
}
|
||||
@@ -704,11 +704,12 @@ int svc_conflicts(svc_t *svc)
|
||||
* @is_conf: Set when called by service_mark_unavail()
|
||||
* @ident: svc_t identififcation (name:id)
|
||||
* @stmt: if: statement from .conf file
|
||||
+ * @quiet: If non-zero, do not warn in syslog
|
||||
*
|
||||
* Returns:
|
||||
* %TRUE(1) yes, use this svc_t, %FALSE(0) prune
|
||||
*/
|
||||
-int svc_ifthen(int is_conf, const char *ident, char *stmt)
|
||||
+int svc_ifthen(int is_conf, const char *ident, char *stmt, int quiet)
|
||||
{
|
||||
char stmts[MAX_IDENT_LEN];
|
||||
int not = 0;
|
||||
diff --git a/src/svc.h b/src/svc.h
|
||||
index f94a30e5..520aabcc 100644
|
||||
--- a/src/svc.h
|
||||
+++ b/src/svc.h
|
||||
@@ -249,7 +249,7 @@ void svc_prune_bootstrap (void);
|
||||
void svc_enable (svc_t *svc);
|
||||
int svc_enabled (svc_t *svc);
|
||||
int svc_conflicts (svc_t *svc);
|
||||
-int svc_ifthen (int is_conf, const char *ident, char *stmt);
|
||||
+int svc_ifthen (int is_conf, const char *ident, char *stmt, int quiet);
|
||||
|
||||
int svc_parse_jobstr (char *str, size_t len, void *user_data, int (*found)(svc_t *, void *), int (not_found)(char *, char *, void *));
|
||||
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
From ed88469276c569acd9ef16bcc38cd80afd8dea52 Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Sun, 10 Dec 2023 17:32:19 +0100
|
||||
Subject: [PATCH 2/2] Actually silence the log message, missing hunk for
|
||||
previous change
|
||||
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
---
|
||||
src/svc.c | 6 ++++--
|
||||
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/svc.c b/src/svc.c
|
||||
index 6bb90049..773f0ab6 100644
|
||||
--- a/src/svc.c
|
||||
+++ b/src/svc.c
|
||||
@@ -759,11 +759,13 @@ int svc_ifthen(int is_conf, const char *ident, char *stmt, int quiet)
|
||||
|
||||
svc = svc_find_by_str(stmt);
|
||||
if (not && svc) {
|
||||
- logit(LOG_NOTICE, "skipping %s, %s already loaded.", ident, svc_ident(svc, NULL, 0));
|
||||
+ if (!quiet)
|
||||
+ logit(LOG_NOTICE, "skipping %s, %s already loaded.", ident, svc_ident(svc, NULL, 0));
|
||||
return 0;
|
||||
}
|
||||
if (!not && !svc) {
|
||||
- logit(LOG_NOTICE, "skipping %s, %s not available.", ident, stmt);
|
||||
+ if (!quiet)
|
||||
+ logit(LOG_NOTICE, "skipping %s, %s not available.", ident, stmt);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
From c2218236546908ecab5eab4b32e698d7bb3d984a Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Sat, 3 Feb 2024 17:30:30 +0100
|
||||
Subject: [PATCH] shutdown: use cond_clear_noupdate() to prevent nested
|
||||
service_stop()
|
||||
Organization: Addiva Elektronik
|
||||
|
||||
When the system shuts down, or user changes runlevels, we don't have to
|
||||
call cond_clear_update(), because this can lead to nested service_stop()
|
||||
calls, which in turn lead to out of sync progress updates:
|
||||
|
||||
[ .. ] Stopping Foo
|
||||
[ OK ] Stopping Bar
|
||||
[ OK ]
|
||||
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
---
|
||||
src/cond-w.c | 6 +++++-
|
||||
src/service.c | 4 +++-
|
||||
2 files changed, 8 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/cond-w.c b/src/cond-w.c
|
||||
index 2502264..0546282 100644
|
||||
--- a/src/cond-w.c
|
||||
+++ b/src/cond-w.c
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "cond.h"
|
||||
#include "pid.h"
|
||||
#include "service.h"
|
||||
+#include "sm.h"
|
||||
|
||||
struct cond_boot {
|
||||
TAILQ_ENTRY(cond_boot) link;
|
||||
@@ -214,7 +215,10 @@ static int do_delete(const char *fpath, const struct stat *sb, int tflag, struct
|
||||
err(1, "Failed removing condition %s", fpath);
|
||||
|
||||
cond = ptr + strlen(COND_BASE) + 1;
|
||||
- cond_update(cond);
|
||||
+ if (sm_is_in_teardown(&sm))
|
||||
+ cond_clear_noupdate(cond);
|
||||
+ else
|
||||
+ cond_update(cond);
|
||||
|
||||
return 0;
|
||||
}
|
||||
diff --git a/src/service.c b/src/service.c
|
||||
index 8175757..eca9ed0 100644
|
||||
--- a/src/service.c
|
||||
+++ b/src/service.c
|
||||
@@ -2261,7 +2261,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new_state)
|
||||
/* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */
|
||||
if (new_state == SVC_STOPPING_STATE) {
|
||||
dbg("%s is stopping, wait %d sec before sending SIGKILL ...",
|
||||
- svc_ident(svc, NULL, 0), svc->killdelay / 1000);
|
||||
+ svc_ident(svc, NULL, 0), svc->killdelay / 1000);
|
||||
service_timeout_cancel(svc);
|
||||
service_timeout_after(svc, svc->killdelay, service_kill);
|
||||
}
|
||||
@@ -2299,6 +2299,8 @@ static void svc_set_state(svc_t *svc, svc_state_t new_state)
|
||||
if ((old_state == SVC_RUNNING_STATE && new_state == SVC_PAUSED_STATE) ||
|
||||
(old_state == SVC_PAUSED_STATE && new_state == SVC_RUNNING_STATE))
|
||||
; /* only paused during reload, don't clear conds. */
|
||||
+ else if (sm_is_in_teardown(&sm))
|
||||
+ cond_clear_noupdate(cond);
|
||||
else
|
||||
cond_clear(cond);
|
||||
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
From 5998342b3e55638b0f7248383e939a64f53fa8ff Mon Sep 17 00:00:00 2001
|
||||
From: Joachim Wiberg <troglobit@gmail.com>
|
||||
Date: Mon, 26 Feb 2024 08:41:14 +0100
|
||||
Subject: [PATCH] system: drop debug mode from udevd
|
||||
Organization: Addiva Elektronik
|
||||
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
---
|
||||
system/10-hotplug.conf.in | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/system/10-hotplug.conf.in b/system/10-hotplug.conf.in
|
||||
index 49ea081..a4f64b5 100644
|
||||
--- a/system/10-hotplug.conf.in
|
||||
+++ b/system/10-hotplug.conf.in
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
# Check for systemd-udevd and eudev, if we find both, we opt for the latter.
|
||||
service nowarn [S12345789] cgroup.system name:udevd notify:none pid:udevd log /lib/systemd/systemd-udevd -- Device event daemon (udev)
|
||||
-service nowarn [S12345789] cgroup.system name:udevd notify:none pid:udevd log udevd -D -- Device event daemon (udev)
|
||||
+service nowarn [S12345789] cgroup.system name:udevd notify:none pid:udevd log udevd -- Device event daemon (udev)
|
||||
|
||||
# Wait for udevd to start, then trigger coldplug events and module loading.
|
||||
# The last 'settle' call waits for it to finalize processing all uevents.
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
From 709063e8368aeed56fd5775c10d33ca4dc8990a5 Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Hemminger <stephen@networkplumber.org>
|
||||
Date: Fri, 2 Jun 2023 08:51:53 -0700
|
||||
Subject: [PATCH] ipaddress: accept symbolic names
|
||||
Organization: Addiva Elektronik
|
||||
|
||||
The function rtnl_addproto_a2n() was defined but never used.
|
||||
Use it to allow for symbolic names, and fix the function signatures
|
||||
so protocol value is consistently __u8.
|
||||
|
||||
Fixes: bdb8d8549ed9 ("ip: Support IP address protocol")
|
||||
Cc: petrm@nvidia.com
|
||||
Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
|
||||
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
|
||||
---
|
||||
include/rt_names.h | 4 ++--
|
||||
ip/ipaddress.c | 2 +-
|
||||
lib/rt_names.c | 18 +++++-------------
|
||||
3 files changed, 8 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/include/rt_names.h b/include/rt_names.h
|
||||
index e96d80f3..02750307 100644
|
||||
--- a/include/rt_names.h
|
||||
+++ b/include/rt_names.h
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <asm/types.h>
|
||||
|
||||
const char *rtnl_rtprot_n2a(int id, char *buf, int len);
|
||||
-const char *rtnl_addrprot_n2a(int id, char *buf, int len);
|
||||
+const char *rtnl_addrprot_n2a(__u8 id, char *buf, int len);
|
||||
const char *rtnl_rtscope_n2a(int id, char *buf, int len);
|
||||
const char *rtnl_rttable_n2a(__u32 id, char *buf, int len);
|
||||
const char *rtnl_rtrealm_n2a(int id, char *buf, int len);
|
||||
@@ -14,7 +14,7 @@ const char *rtnl_dsfield_get_name(int id);
|
||||
const char *rtnl_group_n2a(int id, char *buf, int len);
|
||||
|
||||
int rtnl_rtprot_a2n(__u32 *id, const char *arg);
|
||||
-int rtnl_addrprot_a2n(__u32 *id, const char *arg);
|
||||
+int rtnl_addrprot_a2n(__u8 *id, const char *arg);
|
||||
int rtnl_rtscope_a2n(__u32 *id, const char *arg);
|
||||
int rtnl_rttable_a2n(__u32 *id, const char *arg);
|
||||
int rtnl_rtrealm_a2n(__u32 *id, const char *arg);
|
||||
diff --git a/ip/ipaddress.c b/ip/ipaddress.c
|
||||
index c428dd3d..7accbf7d 100644
|
||||
--- a/ip/ipaddress.c
|
||||
+++ b/ip/ipaddress.c
|
||||
@@ -2547,7 +2547,7 @@ static int ipaddr_modify(int cmd, int flags, int argc, char **argv)
|
||||
__u8 proto;
|
||||
|
||||
NEXT_ARG();
|
||||
- if (get_u8(&proto, *argv, 0))
|
||||
+ if (rtnl_addrprot_a2n(&proto, *argv))
|
||||
invarg("\"proto\" value is invalid\n", *argv);
|
||||
addattr8(&req.n, sizeof(req), IFA_PROTO, proto);
|
||||
} else {
|
||||
diff --git a/lib/rt_names.c b/lib/rt_names.c
|
||||
index 51d11fd0..b441e98f 100644
|
||||
--- a/lib/rt_names.c
|
||||
+++ b/lib/rt_names.c
|
||||
@@ -242,9 +242,9 @@ static void rtnl_addrprot_initialize(void)
|
||||
rtnl_addrprot_tab_initialized = true;
|
||||
}
|
||||
|
||||
-const char *rtnl_addrprot_n2a(int id, char *buf, int len)
|
||||
+const char *rtnl_addrprot_n2a(__u8 id, char *buf, int len)
|
||||
{
|
||||
- if (id < 0 || id >= 256 || numeric)
|
||||
+ if (numeric)
|
||||
goto numeric;
|
||||
if (!rtnl_addrprot_tab_initialized)
|
||||
rtnl_addrprot_initialize();
|
||||
@@ -255,27 +255,19 @@ numeric:
|
||||
return buf;
|
||||
}
|
||||
|
||||
-int rtnl_addrprot_a2n(__u32 *id, const char *arg)
|
||||
+int rtnl_addrprot_a2n(__u8 *id, const char *arg)
|
||||
{
|
||||
- static char *cache;
|
||||
- static unsigned long res;
|
||||
+ unsigned long res;
|
||||
char *end;
|
||||
int i;
|
||||
|
||||
- if (cache && strcmp(cache, arg) == 0) {
|
||||
- *id = res;
|
||||
- return 0;
|
||||
- }
|
||||
-
|
||||
if (!rtnl_addrprot_tab_initialized)
|
||||
rtnl_addrprot_initialize();
|
||||
|
||||
for (i = 0; i < 256; i++) {
|
||||
if (rtnl_addrprot_tab[i] &&
|
||||
strcmp(rtnl_addrprot_tab[i], arg) == 0) {
|
||||
- cache = rtnl_addrprot_tab[i];
|
||||
- res = i;
|
||||
- *id = res;
|
||||
+ *id = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
--
|
||||
2.34.1
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# Host build of critical components, for Coverity Scan mostly
|
||||
APPS = confd statd factory
|
||||
APPS = confd execd factory keyack statd
|
||||
|
||||
.PHONY: alla
|
||||
all:
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
pkglibexec_SCRIPTS = bootstrap error load \
|
||||
gen-hostkeys gen-admin-auth gen-hostname gen-interfaces gen-motd
|
||||
gen-hostkeys gen-admin-auth gen-hostname gen-interfaces gen-motd gen-hardware
|
||||
|
||||
@@ -98,6 +98,7 @@ factory()
|
||||
# Create an overlay for /etc/hostname to change the default in an br2-external
|
||||
gen-hostname >"$FACTORY_D/20-hostname.json"
|
||||
gen-motd >"$FACTORY_D/20-motd.json"
|
||||
gen-hardware >"$FACTORY_D/20-hardware.json"
|
||||
# shellcheck disable=SC2086
|
||||
gen-interfaces $GEN_IFACE_OPTS >"$FACTORY_D/20-interfaces.json"
|
||||
|
||||
@@ -212,21 +213,22 @@ sysrepoctl -s $SEARCH \
|
||||
-i iana-if-type@2023-01-26.yang -g wheel -p 0660 \
|
||||
-i iana-hardware@2018-03-13.yang -g wheel -p 0660 \
|
||||
-i ietf-hardware@2018-03-13.yang -g wheel -p 0660 \
|
||||
-e hardware-state \
|
||||
-i infix-hardware@2024-01-18.yang -g wheel -p 0660 \
|
||||
-i ieee802-dot1q-types@2022-10-29.yang -g wheel -p 0660 \
|
||||
-i infix-ip@2023-09-14.yang -g wheel -p 0660 \
|
||||
-i infix-if-type@2023-08-21.yang -g wheel -p 0660 \
|
||||
-i infix-if-type@2024-01-29.yang -g wheel -p 0660 \
|
||||
-i infix-routing@2024-01-09.yang -g wheel -p 0660 \
|
||||
-i infix-interfaces@2023-09-19.yang -g wheel -p 0660 \
|
||||
-i infix-interfaces@2024-01-15.yang -g wheel -p 0660 \
|
||||
-e vlan-filtering \
|
||||
-i ieee802-dot1ab-lldp@2022-03-15.yang -g wheel -p 0660 \
|
||||
-i infix-lldp@2023-08-23.yang -g wheel -p 0660 \
|
||||
-i infix-dhcp-client@2024-01-30.yang -g wheel -p 0660 \
|
||||
-i infix-shell-type@2023-08-21.yang -g wheel -p 0660 \
|
||||
-i infix-system@2023-10-19.yang -g wheel -p 0660 \
|
||||
-i infix-system@2024-02-29.yang -g wheel -p 0660 \
|
||||
-i infix-services@2023-10-16.yang -g wheel -p 0660 \
|
||||
-i ieee802-ethernet-interface@2019-06-21.yang -g wheel -p 0660 \
|
||||
-i infix-ethernet-interface@2024-01-22.yang -g wheel -p 0660 \
|
||||
-i infix-ethernet-interface@2024-02-27.yang -g wheel -p 0660 \
|
||||
-I "${INIT_DATA}"
|
||||
rc=$?
|
||||
|
||||
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
usb_ports=$(cat /run/system.json | jq -r '.["usb-ports"] | map(.name) | unique | join(" ")')
|
||||
|
||||
gen_port()
|
||||
{
|
||||
local port="$1"
|
||||
cat <<EOF
|
||||
{
|
||||
"class": "infix-hardware:usb",
|
||||
"name": "$port",
|
||||
"state": {
|
||||
"admin-state": "unlocked"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
}
|
||||
first=1
|
||||
cat <<EOF
|
||||
{
|
||||
"ietf-hardware:hardware": {
|
||||
"component": [
|
||||
EOF
|
||||
for port in $usb_ports; do
|
||||
if [ $first -eq 0 ]; then
|
||||
echo -n ','
|
||||
fi
|
||||
first=0;
|
||||
gen_port "$port"
|
||||
done
|
||||
cat <<EOF
|
||||
]}
|
||||
}
|
||||
EOF
|
||||
@@ -57,28 +57,29 @@ EOF
|
||||
}
|
||||
|
||||
# shellcheck disable=SC3043
|
||||
gen_interface()
|
||||
gen_iface_json()
|
||||
{
|
||||
local ifname="$1"
|
||||
local br="$2"
|
||||
local iftype="$1"
|
||||
local ifname="$2"
|
||||
local br="$3"
|
||||
|
||||
cat <<EOF
|
||||
,{
|
||||
"name": "$ifname",
|
||||
"type": "infix-if-type:ethernet",
|
||||
"type": "$iftype",
|
||||
EOF
|
||||
|
||||
if [ -n "$br" ]; then
|
||||
cat <<EOF
|
||||
cat <<EOF
|
||||
"ietf-ip:ipv6": {
|
||||
"enabled": false
|
||||
},
|
||||
"infix-interfaces:bridge-port": {
|
||||
"bridge": "$br"
|
||||
}
|
||||
"enabled": false
|
||||
},
|
||||
"infix-interfaces:bridge-port": {
|
||||
"bridge": "$br"
|
||||
}
|
||||
EOF
|
||||
else
|
||||
cat <<EOF
|
||||
cat <<EOF
|
||||
"ietf-ip:ipv6": {}
|
||||
EOF
|
||||
fi
|
||||
@@ -88,52 +89,71 @@ EOF
|
||||
EOF
|
||||
}
|
||||
|
||||
filter_iface_ports()
|
||||
{
|
||||
ifaces="$1"
|
||||
ports="$2"
|
||||
|
||||
iface_devs=""
|
||||
for phy in $ifaces; do
|
||||
found=""
|
||||
for port in $ports; do
|
||||
if [ "$port" = "$phy" ]; then
|
||||
found=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -z "$found" ]; then
|
||||
iface_devs="$iface_devs $phy"
|
||||
fi
|
||||
done
|
||||
echo -n "$iface_devs"
|
||||
}
|
||||
|
||||
while [ "$1" != "" ]; do
|
||||
case $1 in
|
||||
-4)
|
||||
ipv4=true
|
||||
;;
|
||||
-b)
|
||||
bridge="$2"
|
||||
shift
|
||||
;;
|
||||
-d)
|
||||
dhcp=true
|
||||
;;
|
||||
-h)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
-4)
|
||||
ipv4=true
|
||||
;;
|
||||
-b)
|
||||
bridge="$2"
|
||||
shift
|
||||
;;
|
||||
-d)
|
||||
dhcp=true
|
||||
;;
|
||||
-h)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
phys_ifaces=$(ip -d -j link show | jq -r '
|
||||
.[] |
|
||||
select(.link_type == "ether") |
|
||||
select(.group != "internal") |
|
||||
select(has("parentbus")) |
|
||||
.ifname' | sort -V)
|
||||
.[] |
|
||||
select(.link_type == "ether") |
|
||||
select(.group != "internal") |
|
||||
select(has("parentbus")) |
|
||||
select(.parentbus != "virtio") |
|
||||
.ifname' | sort -V)
|
||||
virtio_ifaces=$(ip -d -j link show | jq -r '
|
||||
.[] |
|
||||
select(.link_type == "ether") |
|
||||
select(.group != "internal") |
|
||||
select(has("parentbus")) |
|
||||
select(.parentbus == "virtio") |
|
||||
.ifname' | sort -V)
|
||||
ports=$(ip -d -j link show group port | jq -r '
|
||||
.[] |
|
||||
select(.link_type == "ether") |
|
||||
select(.group != "internal") |
|
||||
select(has("parentbus")) |
|
||||
.ifname' | sort -V)
|
||||
ifaces=""
|
||||
for phy in $phys_ifaces; do
|
||||
found=""
|
||||
for port in $ports; do
|
||||
if [ "$port" = "$phy" ]; then
|
||||
found=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -z "$found" ]; then
|
||||
ifaces="$ifaces $phy"
|
||||
fi
|
||||
done
|
||||
.[] |
|
||||
select(.link_type == "ether") |
|
||||
select(.group != "internal") |
|
||||
select(has("parentbus")) |
|
||||
.ifname' | sort -V)
|
||||
|
||||
eth_ifaces="$(filter_iface_ports "$phys_ifaces" "$ports")"
|
||||
ethlike_ifaces="$(filter_iface_ports "$virtio_ifaces" "$ports")"
|
||||
|
||||
cat <<EOF
|
||||
{
|
||||
@@ -156,20 +176,21 @@ if [ -n "$bridge" ]; then
|
||||
"name": "$bridge",
|
||||
"type": "infix-if-type:bridge",
|
||||
"ietf-ip:ipv4": {
|
||||
"infix-ip:autoconf": {
|
||||
"infix-ip:autoconf": {
|
||||
"enabled": $ipv4
|
||||
}
|
||||
},
|
||||
"ietf-ip:ipv6": {
|
||||
"enabled": true
|
||||
"ietf-ip:ipv6": {
|
||||
"enabled": true
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
}
|
||||
$(for iface in $ifaces; do gen_interface "$iface"; done)
|
||||
$(for iface in $ports; do gen_interface "$iface" "$bridge"; done)
|
||||
$(for iface in $eth_ifaces; do gen_iface_json "infix-if-type:ethernet" "$iface"; done)
|
||||
$(for iface in $ethlike_ifaces; do gen_iface_json "infix-if-type:etherlike" "$iface"; done)
|
||||
$(for iface in $ports; do gen_iface_json "infix-if-type:ethernet" "$iface" "$bridge"; done)
|
||||
]
|
||||
EOF
|
||||
if [ "$dhcp" = "true" ] && [ -n "$bridge" ]; then
|
||||
|
||||
@@ -6,7 +6,7 @@ if [ -f /etc/motd ]; then
|
||||
cat <<EOF
|
||||
{
|
||||
"ietf-system:system": {
|
||||
"infix-system:motd": "$(sed ':a;N;$!ba;s/\n/\\n/g;s/\t/\\t/g' /etc/motd)"
|
||||
"infix-system:motd-banner": "$(base64 -w0 /etc/motd)"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
@@ -21,11 +21,20 @@ AC_CONFIG_LIBOBJ_DIR(lib)
|
||||
AC_REPLACE_FUNCS(vasprintf)
|
||||
AC_REPLACE_FUNCS(asprintf)
|
||||
|
||||
#
|
||||
# Check feature flags
|
||||
#
|
||||
AC_ARG_ENABLE(containers,
|
||||
AS_HELP_STRING([--enable-containers], [Enable support for containers]),,[
|
||||
enable_containers=no])
|
||||
|
||||
AC_ARG_WITH(login-shell,
|
||||
AS_HELP_STRING([--with-login-shell=shell], [Login shell for new users, default: /bin/false]),
|
||||
[login_shell=$withval], [login_shell=yes])
|
||||
|
||||
AS_IF([test "x$enable_containers" = "xyes"], [
|
||||
AC_DEFINE(CONTAINERS, 1, [Built with container support])])
|
||||
|
||||
AS_IF([test "x$with_login_shell" != "xno"], [
|
||||
AS_IF([test "x$login_shell" = "xyes"], [login_shell=/bin/false])
|
||||
AC_DEFINE_UNQUOTED(LOGIN_SHELL, "$login_shell", [Default: /bin/false])],[
|
||||
@@ -41,6 +50,9 @@ PKG_CHECK_MODULES([libite], [libite >= 2.5.0])
|
||||
PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.36])
|
||||
PKG_CHECK_MODULES([libsrx], [libsrx >= 1.0.0])
|
||||
|
||||
# Control build with automake flags
|
||||
AM_CONDITIONAL(CONTAINERS, [test "x$enable_containers" != "xno"])
|
||||
|
||||
# Plugin installation path for sysrepo-plugind
|
||||
PKG_CHECK_VAR([srpdplugindir], [sysrepo], [SRPD_PLUGINS_PATH])
|
||||
AC_SUBST(srpdplugindir)
|
||||
@@ -72,3 +84,31 @@ SYSCONFDIR=`eval echo $SYSCONFDIR`
|
||||
AC_SUBST(SYSCONFDIR)
|
||||
|
||||
AC_OUTPUT
|
||||
|
||||
cat <<EOF
|
||||
|
||||
------------------ Summary ------------------
|
||||
$PACKAGE_NAME version $PACKAGE_VERSION
|
||||
Prefix................: $prefix
|
||||
Exec prefix...........: $eprefix
|
||||
Sysconfdir............: `eval echo $sysconfdir`
|
||||
Localstatedir.........: `eval echo $localstatedir`
|
||||
Default fstab.........: `eval echo $fstab`
|
||||
System environment....: ${sysconfig_path:-${sysconfig}}
|
||||
C Compiler............: $CC $CFLAGS $CPPFLAGS $LDFLAGS $LIBS
|
||||
Linker................: $LD $LLDP_LDFLAGS $LLDP_BIN_LDFLAGS $LDFLAGS $LIBS
|
||||
|
||||
Optional features:
|
||||
Container support ....: $enable_containers
|
||||
Login shell ..........: $login_shell
|
||||
|
||||
------------- Compiler version --------------
|
||||
$($CC --version || true)
|
||||
-------------- Linker version ---------------
|
||||
$($LD --version || true)
|
||||
---------------------------------------------
|
||||
|
||||
Check the above options and compile with:
|
||||
${MAKE-make}
|
||||
|
||||
EOF
|
||||
|
||||
+18
-13
@@ -1,4 +1,3 @@
|
||||
CFLAGS += -Wall -Wextra -Werror -Wno-unused-parameter
|
||||
AM_CPPFLAGS = -D_DEFAULT_SOURCE -D_XOPEN_SOURCE -D_GNU_SOURCE -DYANG_PATH_=\"$(YANGDIR)\"
|
||||
CLEANFILES = $(rauc_installer_sources)
|
||||
plugindir = $(srpdplugindir)
|
||||
@@ -7,12 +6,12 @@ plugin_LTLIBRARIES = confd-plugin.la
|
||||
confd_plugin_la_LDFLAGS = -module -avoid-version -shared
|
||||
|
||||
confd_plugin_la_CFLAGS = \
|
||||
$(augeas_CFLAGS) \
|
||||
$(augeas_CFLAGS) \
|
||||
$(glib_CFLAGS) \
|
||||
$(jansson_CFLAGS) \
|
||||
$(libite_CFLAGS) \
|
||||
$(sysrepo_CFLAGS) \
|
||||
$(libsrx_CFLAGS) \
|
||||
$(jansson_CFLAGS) \
|
||||
$(libite_CFLAGS) \
|
||||
$(sysrepo_CFLAGS) \
|
||||
$(libsrx_CFLAGS) \
|
||||
$(CFLAGS)
|
||||
|
||||
confd_plugin_la_LIBADD = \
|
||||
@@ -24,16 +23,22 @@ confd_plugin_la_LIBADD = \
|
||||
$(libsrx_LIBS)
|
||||
|
||||
confd_plugin_la_SOURCES = \
|
||||
core.c core.h \
|
||||
dagger.c dagger.h \
|
||||
ietf-interfaces.c \
|
||||
base64.c base64.h \
|
||||
core.c core.h \
|
||||
dagger.c dagger.h \
|
||||
ietf-interfaces.c \
|
||||
ietf-system.c \
|
||||
ietf-factory-default.c \
|
||||
ietf-routing.c \
|
||||
infix-dhcp.c \
|
||||
infix-services.c \
|
||||
infix-services.c \
|
||||
infix-factory.c \
|
||||
infix-system-software.c
|
||||
infix-system-software.c \
|
||||
ietf-hardware.c
|
||||
|
||||
if CONTAINERS
|
||||
confd_plugin_la_SOURCES += infix-containers.c cni.c cni.h
|
||||
endif
|
||||
|
||||
rauc_installer_sources = \
|
||||
rauc-installer.c rauc-installer.h
|
||||
@@ -44,9 +49,9 @@ EXTRA_DIST = de.pengutronix.rauc.Installer.xml
|
||||
$(rauc_installer_sources): de.pengutronix.rauc.Installer.xml
|
||||
$(AM_V_GEN) gdbus-codegen \
|
||||
--generate-c-code rauc-installer \
|
||||
--c-generate-autocleanup all \
|
||||
--c-generate-autocleanup all \
|
||||
--c-namespace Rauc \
|
||||
--interface-prefix de.pengutronix.rauc. \
|
||||
--interface-prefix de.pengutronix.rauc. \
|
||||
$<
|
||||
|
||||
nodist_confd_plugin_la_SOURCES = $(rauc_installer_sources)
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "base64.h"
|
||||
|
||||
/*
|
||||
* Base64 encoding/decoding (RFC1341)
|
||||
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
|
||||
*/
|
||||
static const unsigned char base64_table[65] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/**
|
||||
* base64_encode - Base64 encode
|
||||
* @src: Data to be encoded
|
||||
* @len: Length of the data to be encoded
|
||||
* @out_len: Pointer to output length variable, or %NULL if not used
|
||||
*
|
||||
* Returns:
|
||||
* Allocated buffer of out_len bytes of encoded data, %NULL on failure
|
||||
*
|
||||
* Caller is responsible for freeing the returned buffer. Returned
|
||||
* buffer is nul terminated to make it easier to use as a C string.
|
||||
* The nul terminator is not included in out_len.
|
||||
*/
|
||||
unsigned char *base64_encode(const unsigned char *src, size_t len, size_t *out_len)
|
||||
{
|
||||
unsigned char *out, *pos;
|
||||
const unsigned char *end, *in;
|
||||
size_t olen;
|
||||
int line_len;
|
||||
|
||||
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
|
||||
olen += olen / 72; /* line feeds */
|
||||
olen++; /* nul termination */
|
||||
if (olen < len)
|
||||
return NULL; /* integer overflow */
|
||||
out = malloc(olen);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
end = src + len;
|
||||
in = src;
|
||||
pos = out;
|
||||
line_len = 0;
|
||||
while (end - in >= 3) {
|
||||
*pos++ = base64_table[in[0] >> 2];
|
||||
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
|
||||
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
|
||||
*pos++ = base64_table[in[2] & 0x3f];
|
||||
in += 3;
|
||||
line_len += 4;
|
||||
if (line_len >= 72) {
|
||||
*pos++ = '\n';
|
||||
line_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (end - in) {
|
||||
*pos++ = base64_table[in[0] >> 2];
|
||||
if (end - in == 1) {
|
||||
*pos++ = base64_table[(in[0] & 0x03) << 4];
|
||||
*pos++ = '=';
|
||||
} else {
|
||||
*pos++ = base64_table[((in[0] & 0x03) << 4) |
|
||||
(in[1] >> 4)];
|
||||
*pos++ = base64_table[(in[1] & 0x0f) << 2];
|
||||
}
|
||||
*pos++ = '=';
|
||||
line_len += 4;
|
||||
}
|
||||
|
||||
if (line_len)
|
||||
*pos++ = '\n';
|
||||
|
||||
*pos = '\0';
|
||||
if (out_len)
|
||||
*out_len = pos - out;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* base64_decode - Base64 decode
|
||||
* @src: Data to be decoded
|
||||
* @len: Length of the data to be decoded
|
||||
* @out_len: Pointer to output length variable
|
||||
*
|
||||
* Returns:
|
||||
* Allocated buffer of out_len bytes of decoded data, or %NULL on failure
|
||||
* Caller is responsible for freeing the returned buffer.
|
||||
*/
|
||||
unsigned char *base64_decode(const unsigned char *src, size_t len, size_t *out_len)
|
||||
{
|
||||
unsigned char dtable[256], *out, *pos, block[4], tmp;
|
||||
size_t i, count, olen;
|
||||
int pad = 0;
|
||||
|
||||
memset(dtable, 0x80, 256);
|
||||
for (i = 0; i < sizeof(base64_table) - 1; i++)
|
||||
dtable[base64_table[i]] = (unsigned char) i;
|
||||
dtable['='] = 0;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
if (dtable[src[i]] != 0x80)
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count == 0 || count % 4) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
olen = count / 4 * 3;
|
||||
pos = out = malloc(olen);
|
||||
if (out == NULL)
|
||||
return NULL;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
tmp = dtable[src[i]];
|
||||
if (tmp == 0x80)
|
||||
continue;
|
||||
|
||||
if (src[i] == '=')
|
||||
pad++;
|
||||
block[count] = tmp;
|
||||
count++;
|
||||
if (count == 4) {
|
||||
*pos++ = (block[0] << 2) | (block[1] >> 4);
|
||||
*pos++ = (block[1] << 4) | (block[2] >> 2);
|
||||
*pos++ = (block[2] << 6) | block[3];
|
||||
count = 0;
|
||||
if (pad) {
|
||||
if (pad == 1)
|
||||
pos--;
|
||||
else if (pad == 2)
|
||||
pos -= 2;
|
||||
else {
|
||||
/* Invalid padding */
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (out_len)
|
||||
*out_len = pos - out;
|
||||
return out;
|
||||
}
|
||||
|
||||
#ifdef UNITTEST
|
||||
#include <err.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int c, decode = 0, bytes;
|
||||
char *buf, *ptr;
|
||||
struct stat st;
|
||||
size_t len;
|
||||
FILE *fp;
|
||||
|
||||
while ((c = getopt(argc, argv, "dh")) != EOF) {
|
||||
switch (c) {
|
||||
case 'd':
|
||||
decode = 1;
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
printf("%s [-d] file", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc <= optind)
|
||||
errx(1, "Missing file arg.");
|
||||
|
||||
fp = fopen(argv[optind], "r");
|
||||
if (!fp)
|
||||
err(1, "failed opening %s", argv[optind]);
|
||||
if (fstat(fileno(fp), &st))
|
||||
err(1, "failed fstat %s", argv[optind]);
|
||||
len = st.st_size;
|
||||
buf = malloc(len);
|
||||
|
||||
bytes = fread(buf, 1, len, fp);
|
||||
if (bytes != (int)len)
|
||||
err(1, "failed reading entire file");
|
||||
|
||||
if (decode)
|
||||
ptr = base64_decode(buf, bytes, NULL);
|
||||
else
|
||||
ptr = base64_encode(buf, bytes, NULL);
|
||||
|
||||
puts(ptr);
|
||||
free(ptr);
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#ifndef BASE64_H_
|
||||
#define BASE64_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
unsigned char *base64_encode(const unsigned char *src, size_t len, size_t *out_len);
|
||||
unsigned char *base64_decode(const unsigned char *src, size_t len, size_t *out_len);
|
||||
|
||||
#endif /* BASE64_H_ */
|
||||
@@ -0,0 +1,450 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
#include <net/if.h>
|
||||
#include <srx/common.h>
|
||||
#include <srx/lyx.h>
|
||||
#include <srx/srx_val.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "cni.h"
|
||||
|
||||
#define CNI_NAME "/etc/cni/net.d/%s.conflist"
|
||||
|
||||
/*
|
||||
* When an interface has been handed off to a container it is moved to
|
||||
* another network namespace. This function asks podman for the PID of
|
||||
* the container that currently hosts the interface.
|
||||
*/
|
||||
pid_t cni_find(const char *ifname)
|
||||
{
|
||||
char buf[32] = { 0 };
|
||||
pid_t pid = 0;
|
||||
FILE *pp;
|
||||
|
||||
if (fexistf("/sys/class/net/%s", ifname))
|
||||
return 0; /* it's right here, not in a container */
|
||||
|
||||
pp = popenf("r", "container --net %s find", ifname);
|
||||
if (!pp)
|
||||
return 0;
|
||||
|
||||
if (fgets(buf, sizeof(buf), pp)) {
|
||||
chomp(buf);
|
||||
pid = atoi(buf);
|
||||
}
|
||||
|
||||
pclose(pp);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function takes the PID from cni_find() and figures out
|
||||
* the name of our ifname inside the container. For CNI/podman they
|
||||
* save the host's name as the interface's ifalias.
|
||||
*/
|
||||
static char *cni_find_ifalias(pid_t pid, const char *ifname)
|
||||
{
|
||||
static char buf[IFNAMSIZ + 2];
|
||||
char *ptr = NULL;
|
||||
FILE *pp;
|
||||
|
||||
pp = popenf("r", "container --net %s find ifname %d", ifname, pid);
|
||||
if (!pp)
|
||||
return NULL;
|
||||
|
||||
buf[0] = 0;
|
||||
if (fgets(buf, sizeof(buf), pp)) {
|
||||
chomp(buf);
|
||||
ptr = buf;
|
||||
}
|
||||
pclose(pp);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sometimes we need to perform some kind of 'ip link dev IFNAME'
|
||||
* command to find buried interface data. This function handles
|
||||
* the fact that interfaces sometimes are on vacation in another
|
||||
* network namespace (container).
|
||||
*/
|
||||
FILE *cni_popen(const char *fmt, const char *ifname)
|
||||
{
|
||||
char cmd[strlen(fmt) + 64];
|
||||
static char *ifalias;
|
||||
pid_t pid;
|
||||
|
||||
pid = cni_find(ifname);
|
||||
if (!pid)
|
||||
return popenf("re", fmt, ifname);
|
||||
|
||||
ifalias = cni_find_ifalias(pid, ifname);
|
||||
if (!ifalias)
|
||||
return NULL;
|
||||
|
||||
snprintf(cmd, sizeof(cmd), fmt, ifalias);
|
||||
return popenf("re", "nsenter -t %d -n %s", pid, cmd);
|
||||
}
|
||||
|
||||
static bool iface_is_cni(const char *ifname, struct lyd_node *node, const char **type)
|
||||
{
|
||||
struct lyd_node *net = lydx_get_child(node, "container-network");
|
||||
|
||||
if (net) {
|
||||
if (type)
|
||||
*type = lydx_get_cattr(net, "type");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void cni_gen_addrs(struct lyd_node *ip, FILE *fp, int *ipam)
|
||||
{
|
||||
struct lyd_node *addr;
|
||||
|
||||
if (!lydx_is_enabled(ip, "enabled"))
|
||||
return;
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(ip), addr, "address") {
|
||||
struct lyd_node *ip = lydx_get_child(addr, "ip");
|
||||
struct lyd_node *len = lydx_get_child(addr, "prefix-length");
|
||||
|
||||
if (*ipam == 0)
|
||||
fprintf(fp,
|
||||
",\n \"ipam\": {\n"
|
||||
" \"type\": \"static\",\n"
|
||||
" \"addresses\": [\n");
|
||||
|
||||
fprintf(fp, "%s { \"address\": \"%s/%s\" }",
|
||||
*ipam ? ",\n" : "", lyd_get_value(ip), lyd_get_value(len));
|
||||
*ipam = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 /* Unused for now, use container specific global dns and search settings instead. */
|
||||
static void cni_gen_dns(struct lyd_node *net, FILE *fp, int *first)
|
||||
{
|
||||
struct lyd_node *dns;
|
||||
|
||||
dns = lydx_get_child(net, "dns");
|
||||
if (dns) {
|
||||
struct lyd_node *node;
|
||||
|
||||
fprintf(fp, ",\n \"dns\": {");
|
||||
|
||||
*first = 1;
|
||||
LYX_LIST_FOR_EACH(lyd_child(dns), node, "nameservers") {
|
||||
if (*first)
|
||||
fprintf(fp, "\n \"nameservers\": [ ");
|
||||
else
|
||||
fprintf(fp, ", ");
|
||||
|
||||
fprintf(fp, "\"%s\"", lyd_get_value(node));
|
||||
(*first)++;
|
||||
}
|
||||
if (*first > 1)
|
||||
fprintf(fp, " ]");
|
||||
|
||||
node = lydx_get_child(dns, "domain");
|
||||
if (node) {
|
||||
fprintf(fp, "%s\n \"domain\": \"%s\"",
|
||||
*first > 1 ? "," : "", lyd_get_value(node));
|
||||
(*first)++;
|
||||
}
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(net), node, "search") {
|
||||
if (*first)
|
||||
fprintf(fp, "%s\n \"search\": [ ", *first > 1 ? "," : "");
|
||||
else
|
||||
fprintf(fp, ", ");
|
||||
|
||||
fprintf(fp, "\"%s\"", lyd_get_value(node));
|
||||
*first = 0;
|
||||
}
|
||||
|
||||
fprintf(fp, "%s\n }", *first ? "" : "]");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Set up IP masquerading bridge which acts as a gateway for nodes behind it.
|
||||
* Default subnet, if one is missing in configuration, is: 10.88.0.0/16
|
||||
*/
|
||||
static int cni_bridge(struct lyd_node *net, const char *ifname)
|
||||
{
|
||||
struct lyd_node *node;
|
||||
int first = 1;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("w", CNI_NAME, ifname);
|
||||
if (!fp) {
|
||||
ERRNO("Failed creating container bridge " CNI_NAME, ifname);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
fprintf(fp, "{\n"
|
||||
" \"cniVersion\": \"1.0.0\",\n"
|
||||
" \"name\": \"%s\",\n"
|
||||
" \"plugins\": [\n"
|
||||
" {\n"
|
||||
" \"type\": \"bridge\",\n"
|
||||
" \"bridge\": \"%s\",\n"
|
||||
" \"isGateway\": true,\n"
|
||||
" \"ipMasq\": true,\n"
|
||||
" \"hairpinMode\": true,\n"
|
||||
// " \"dataDir\": \"/run/containers/networks\",\n"
|
||||
" \"ipam\": {\n"
|
||||
" \"type\": \"host-local\"", ifname, ifname);
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(net), node, "route") {
|
||||
struct lyd_node *subnet = lydx_get_child(node, "subnet");
|
||||
struct lyd_node *gateway = lydx_get_child(node, "gateway");
|
||||
|
||||
if (first)
|
||||
fprintf(fp, ",\n \"routes\": [\n");
|
||||
else
|
||||
fprintf(fp, ",\n");
|
||||
|
||||
fprintf(fp, " {\n"
|
||||
" \"dst\": \"%s\"%s\n", lyd_get_value(subnet), gateway ? "," : "");
|
||||
if (gateway)
|
||||
fprintf(fp, " \"gw\": \"%s\"\n", lyd_get_value(gateway));
|
||||
fprintf(fp, " }");
|
||||
|
||||
first = 0;
|
||||
}
|
||||
if (!first)
|
||||
fprintf(fp, " ]");
|
||||
else
|
||||
fprintf(fp, ",\n \"routes\": [ { \"dst\": \"0.0.0.0/0\" } ]");
|
||||
|
||||
first = 1;
|
||||
LYX_LIST_FOR_EACH(lyd_child(net), node, "subnet") {
|
||||
struct lyd_node *subnet = lydx_get_child(node, "subnet");
|
||||
struct lyd_node *gateway = lydx_get_child(node, "gateway");
|
||||
|
||||
if (first)
|
||||
fprintf(fp, ",\n \"ranges\": [\n");
|
||||
else
|
||||
fprintf(fp, ",\n");
|
||||
|
||||
fprintf(fp, " [{\n"
|
||||
" \"subnet\": \"%s\"%s\n", lyd_get_value(subnet), gateway ? "," : "");
|
||||
if (gateway)
|
||||
fprintf(fp, " \"gateway\": \"%s\"\n", lyd_get_value(gateway));
|
||||
fprintf(fp, " }]");
|
||||
|
||||
first = 0;
|
||||
}
|
||||
if (!first)
|
||||
fprintf(fp, "\n ]");
|
||||
else
|
||||
/* Default is a customary docker0 local network */
|
||||
fprintf(fp, ",\n \"ranges\": [ [{ \"subnet\": \"172.17.0.0/16\" }] ]");
|
||||
|
||||
fprintf(fp,
|
||||
"\n }\n" /* /ipam */
|
||||
" },\n" /* /bridge */
|
||||
" {\n"
|
||||
" \"type\": \"portmap\",\n"
|
||||
" \"capabilities\": {\n"
|
||||
" \"portMappings\": true\n"
|
||||
" }\n"
|
||||
" },\n" /* /portmap */
|
||||
" {\n"
|
||||
" \"type\": \"firewall\"\n"
|
||||
" },\n" /* /firewall */
|
||||
" {\n"
|
||||
" \"type\": \"tuning\"\n"
|
||||
" }\n" /* /tuning */
|
||||
" ]\n"
|
||||
"}\n");
|
||||
|
||||
if (fclose(fp))
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cni_host(struct lyd_node *net, const char *ifname)
|
||||
{
|
||||
struct lyd_node *node, *ip;
|
||||
int addr = 0, route = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopenf("w", CNI_NAME, ifname);
|
||||
if (!fp) {
|
||||
ERRNO("Failed creating container interface " CNI_NAME, ifname);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
fprintf(fp, "{\n"
|
||||
" \"cniVersion\": \"1.0.0\",\n"
|
||||
" \"name\": \"%s\",\n"
|
||||
" \"plugins\": [\n"
|
||||
" {\n"
|
||||
" \"type\": \"host-device\",\n"
|
||||
" \"device\": \"%s\"", ifname, ifname);
|
||||
|
||||
|
||||
ip = lydx_get_child(lyd_parent(net), "ipv4");
|
||||
if (ip)
|
||||
cni_gen_addrs(ip, fp, &addr);
|
||||
|
||||
ip = lydx_get_child(lyd_parent(net), "ipv6");
|
||||
if (ip)
|
||||
cni_gen_addrs(ip, fp, &addr);
|
||||
|
||||
if (addr)
|
||||
fprintf(fp, "\n ]");
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(net), node, "route") {
|
||||
struct lyd_node *subnet = lydx_get_child(node, "subnet");
|
||||
struct lyd_node *gateway = lydx_get_child(node, "gateway");
|
||||
|
||||
if (route)
|
||||
fprintf(fp, ",\n \"routes\": [\n");
|
||||
else
|
||||
fprintf(fp, ",\n");
|
||||
|
||||
fprintf(fp, " {\n"
|
||||
" \"dst\": \"%s\"%s\n", lyd_get_value(subnet), gateway ? "," : "");
|
||||
if (gateway)
|
||||
fprintf(fp, " \"gw\": \"%s\"\n", lyd_get_value(gateway));
|
||||
fprintf(fp, " }");
|
||||
|
||||
route = 0;
|
||||
}
|
||||
if (route)
|
||||
fprintf(fp, " ]");
|
||||
|
||||
fprintf(fp,
|
||||
"%s"
|
||||
" }\n"
|
||||
" ]\n"
|
||||
"}\n", (addr || route) ? "\n }\n" : "");
|
||||
|
||||
if (fclose(fp))
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iface_gen_cni(const char *ifname, struct lyd_node *cif)
|
||||
{
|
||||
struct lyd_node *net = lydx_get_child(cif, "container-network");
|
||||
const char *type = lydx_get_cattr(net, "type");
|
||||
|
||||
/*
|
||||
* klish/sysrepo does not seem to call update callbacks for
|
||||
* presence containers, so we have to be prepared for the
|
||||
* worst here and perform late type inference. What works:
|
||||
*
|
||||
* edit container-network # callback called
|
||||
*
|
||||
* What doesn't work:
|
||||
*
|
||||
* set container-network # callback not called
|
||||
*
|
||||
* Funnily enough, "show running-config" shows the empty
|
||||
* "container-network": {}, so someone does their job.
|
||||
*/
|
||||
if (!type) {
|
||||
const char *iftype = lydx_get_cattr(cif, "type");
|
||||
|
||||
if (iftype && !strcmp(iftype, "infix-if-type:bridge"))
|
||||
type = "bridge";
|
||||
else
|
||||
type = "host";
|
||||
}
|
||||
|
||||
if (!strcmp(type, "host"))
|
||||
return cni_host(net, ifname);
|
||||
|
||||
if (!strcmp(type, "bridge"))
|
||||
return cni_bridge(net, ifname);
|
||||
|
||||
ERROR("Unknown container network type %s, skipping.", type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cni_netdag_gen_iface(struct dagger *net, const char *ifname,
|
||||
struct lyd_node *dif, struct lyd_node *cif)
|
||||
{
|
||||
const char *cni_type = NULL;
|
||||
|
||||
if (iface_is_cni(ifname, cif, &cni_type)) {
|
||||
int err;
|
||||
|
||||
err = iface_gen_cni(ifname, cif);
|
||||
if (err)
|
||||
return err;
|
||||
if (cni_type && !strcmp(cni_type, "bridge"))
|
||||
return 1; /* CNI bridges are managed by podman */
|
||||
} else if (iface_is_cni(ifname, dif, &cni_type)) {
|
||||
FILE *fp;
|
||||
|
||||
/* No longer a container-network, clean up. */
|
||||
fp = dagger_fopen_current(net, "exit", ifname, 30, "cni.sh");
|
||||
if (!fp)
|
||||
return -EIO;
|
||||
|
||||
fprintf(fp, "container -a -f delete network %s >/dev/null\n", ifname);
|
||||
fclose(fp);
|
||||
|
||||
if (cni_type && !strcmp(cni_type, "bridge"))
|
||||
return 1; /* CNI bridges are managed by podman */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cni_ifchange_cand_infer_type(sr_session_ctx_t *session, const char *path)
|
||||
{
|
||||
sr_val_t inferred = { .type = SR_STRING_T };
|
||||
struct lyd_node *node, *net;
|
||||
sr_error_t err = SR_ERR_OK;
|
||||
char *xpath, *iftype;
|
||||
sr_data_t *cfg;
|
||||
|
||||
xpath = xpath_base(path);
|
||||
if (!xpath)
|
||||
return SR_ERR_SYS;
|
||||
|
||||
err = sr_get_data(session, path, 0, 0, 0, &cfg);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
node = lydx_get_descendant(cfg->tree, "interfaces", "interface", NULL);
|
||||
if (!node)
|
||||
goto out;
|
||||
|
||||
net = lydx_get_child(node, "container-network");
|
||||
if (!net)
|
||||
goto out;
|
||||
|
||||
if (lydx_get_cattr(net, "type"))
|
||||
goto out; /* CNI type is already set */
|
||||
|
||||
/* Infer from ietf-interface type, reduces typing */
|
||||
iftype = srx_get_str(session, "%s/type", xpath);
|
||||
if (iftype && !strcmp(iftype, "infix-if-type:bridge"))
|
||||
inferred.data.string_val = "bridge";
|
||||
else
|
||||
inferred.data.string_val = "host";
|
||||
|
||||
err = srx_set_item(session, &inferred, 0, "%s/type", path);
|
||||
if (err)
|
||||
ERROR("failed setting container-network type %s, err %d: %s",
|
||||
inferred.data.string_val, err, sr_strerror(err));
|
||||
if (iftype)
|
||||
free(iftype);
|
||||
out:
|
||||
sr_release_data(cfg);
|
||||
err:
|
||||
free(xpath);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#ifndef CONFD_CNI_H_
|
||||
#define CONFD_CNI_H_
|
||||
#include "dagger.h"
|
||||
|
||||
#ifdef CONTAINERS
|
||||
|
||||
pid_t cni_find(const char *ifname);
|
||||
FILE *cni_popen(const char *fmt, const char *ifname);
|
||||
|
||||
int cni_netdag_gen_iface(struct dagger *net, const char *ifname, struct lyd_node *dif, struct lyd_node *cif);
|
||||
int cni_ifchange_cand_infer_type(sr_session_ctx_t *session, const char *path);
|
||||
|
||||
#else
|
||||
static inline pid_t cni_find(const char *ifname) { return 0; }
|
||||
static inline FILE *cni_popen(const char *fmt, const char *ifname) { return popenf("re", fmt, ifname); }
|
||||
|
||||
static inline int cni_netdag_gen_iface(struct dagger *net, const char *ifname, struct lyd_node *dif, struct lyd_node *cif) { return 0; }
|
||||
static inline int cni_ifchange_cand_infer_type(sr_session_ctx_t *session, const char *path) { return 0; }
|
||||
#endif
|
||||
|
||||
#endif /* CONFD_CNI_H_ */
|
||||
+43
-12
@@ -15,7 +15,7 @@ uint32_t core_hook_prio(void)
|
||||
}
|
||||
|
||||
int core_startup_save(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
|
||||
{
|
||||
if (systemf("sysrepocfg -X/cfg/startup-config.cfg -d startup -f json"))
|
||||
return SR_ERR_SYS;
|
||||
@@ -23,8 +23,24 @@ int core_startup_save(sr_session_ctx_t *session, uint32_t sub_id, const char *mo
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static const char *ev2str(sr_event_t ev)
|
||||
{
|
||||
switch (ev) {
|
||||
case SR_EV_UPDATE: return "UPDATE";
|
||||
case SR_EV_CHANGE: return "CHANGE";
|
||||
case SR_EV_DONE: return "DONE";
|
||||
case SR_EV_ABORT: return "ABORT";
|
||||
case SR_EV_ENABLED: return "ENABLED";
|
||||
case SR_EV_RPC: return "ABORT";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
int core_pre_hook(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -35,7 +51,7 @@ int core_pre_hook(sr_session_ctx_t *session, uint32_t sub_id, const char *module
|
||||
* For details, see https://github.com/sysrepo/sysrepo/issues/2188
|
||||
*/
|
||||
int core_post_hook(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
|
||||
{
|
||||
static int num_changes = 0;
|
||||
|
||||
@@ -51,10 +67,13 @@ int core_post_hook(sr_session_ctx_t *session, uint32_t sub_id, const char *modul
|
||||
break;
|
||||
return SR_ERR_OK;
|
||||
default:
|
||||
ERROR("core_commit_done() should not be called with event %d", event);
|
||||
ERROR("core_post_hook() should not be called with event %s", ev2str(event));
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
/* Everything done, including interfaces, launch all container scripts */
|
||||
infix_containers_post_hook(session, priv);
|
||||
|
||||
/* skip reload in bootstrap, implicit reload in runlevel change */
|
||||
if (systemf("runlevel >/dev/null 2>&1"))
|
||||
return SR_ERR_OK;
|
||||
@@ -83,9 +102,9 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
|
||||
/* Save context with default running config datastore for all our models */
|
||||
*priv = (void *)&confd;
|
||||
confd.session = session;
|
||||
confd.conn = sr_session_get_connection(session);
|
||||
confd.sub = NULL;
|
||||
confd.fsub = NULL;
|
||||
confd.conn = sr_session_get_connection(session);
|
||||
confd.sub = NULL;
|
||||
confd.fsub = NULL;
|
||||
|
||||
if (!confd.conn)
|
||||
goto err;
|
||||
@@ -99,15 +118,20 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
|
||||
rc = sr_session_start(confd.conn, SR_DS_CANDIDATE, &confd.cand);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
confd.aug = aug_init(NULL, "", 0);
|
||||
if (!confd.aug)
|
||||
goto err;
|
||||
|
||||
confd.root = json_load_file("/run/system.json", 0, NULL);
|
||||
if (!confd.root)
|
||||
goto err;
|
||||
rc = ietf_interfaces_init(&confd);
|
||||
if (rc)
|
||||
goto err;
|
||||
rc = ietf_system_init(&confd);
|
||||
if (rc)
|
||||
goto err;
|
||||
rc = infix_containers_init(&confd);
|
||||
if (rc)
|
||||
goto err;
|
||||
rc = infix_dhcp_init(&confd);
|
||||
@@ -117,23 +141,29 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
|
||||
if (rc)
|
||||
goto err;
|
||||
rc = ietf_factory_default_init(&confd);
|
||||
if(rc)
|
||||
if (rc)
|
||||
goto err;
|
||||
rc = ietf_routing_init(&confd);
|
||||
if(rc)
|
||||
if (rc)
|
||||
goto err;
|
||||
rc = infix_system_sw_init(&confd);
|
||||
if (rc)
|
||||
goto err;
|
||||
rc = infix_services_init(&confd);
|
||||
if (rc)
|
||||
goto err;
|
||||
rc = ietf_hardware_init(&confd);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
/* YOUR_INIT GOES HERE */
|
||||
|
||||
return SR_ERR_OK;
|
||||
|
||||
err:
|
||||
ERROR("init failed: %s", sr_strerror(rc));
|
||||
if (confd.root)
|
||||
json_decref(confd.root);
|
||||
sr_unsubscribe(confd.sub);
|
||||
sr_unsubscribe(confd.fsub);
|
||||
|
||||
@@ -144,7 +174,8 @@ void sr_plugin_cleanup_cb(sr_session_ctx_t *session, void *priv)
|
||||
{
|
||||
struct confd *confd = (struct confd *)priv;
|
||||
|
||||
sr_unsubscribe(confd->sub);
|
||||
sr_unsubscribe(confd->fsub);
|
||||
sr_unsubscribe(confd->sub);
|
||||
sr_unsubscribe(confd->fsub);
|
||||
json_decref(confd->root);
|
||||
closelog();
|
||||
}
|
||||
|
||||
+37
-1
@@ -21,6 +21,8 @@
|
||||
#include <sysrepo/values.h>
|
||||
#include <sysrepo/xpath.h>
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
#include <srx/common.h>
|
||||
#include <srx/helpers.h>
|
||||
#include <srx/systemv.h>
|
||||
@@ -40,6 +42,26 @@ static inline void print_val(sr_val_t *val)
|
||||
free(str);
|
||||
}
|
||||
|
||||
static inline char *xpath_base(const char *xpath)
|
||||
{
|
||||
char *path, *ptr;
|
||||
|
||||
if (!xpath)
|
||||
return NULL;
|
||||
|
||||
path = strdup(xpath);
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
if (!(ptr = strstr(path, "]/"))) {
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
ptr[1] = 0;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#define REGISTER_CHANGE(s,m,x,f,c,a,u) \
|
||||
if ((rc = register_change(s, m, x, f, c, a, u))) \
|
||||
goto fail
|
||||
@@ -59,7 +81,7 @@ struct confd {
|
||||
sr_conn_ctx_t *conn;
|
||||
sr_subscription_ctx_t *sub;
|
||||
sr_subscription_ctx_t *fsub; /* factory-default sub */
|
||||
|
||||
json_t *root;
|
||||
augeas *aug;
|
||||
struct dagger netdag;
|
||||
};
|
||||
@@ -129,6 +151,17 @@ int ietf_interfaces_init(struct confd *confd);
|
||||
/* ietf-system.c */
|
||||
int ietf_system_init(struct confd *confd);
|
||||
|
||||
/* infix-containers.c */
|
||||
#ifdef CONTAINERS
|
||||
int infix_containers_init(struct confd *confd);
|
||||
void infix_containers_pre_hook(sr_session_ctx_t *session, struct confd *confd);
|
||||
void infix_containers_post_hook(sr_session_ctx_t *session, struct confd *confd);
|
||||
#else
|
||||
static inline int infix_containers_init(struct confd *confd) { return 0; }
|
||||
static inline void infix_containers_pre_hook(sr_session_ctx_t *session, struct confd *confd) {}
|
||||
static inline void infix_containers_post_hook(sr_session_ctx_t *session, struct confd *confd) {}
|
||||
#endif
|
||||
|
||||
/* infix-dhcp.c */
|
||||
int infix_dhcp_init(struct confd *confd);
|
||||
|
||||
@@ -147,4 +180,7 @@ int infix_system_sw_init(struct confd *confd);
|
||||
/* infix-services.c */
|
||||
int infix_services_init(struct confd *confd);
|
||||
|
||||
/* ietf-hardware.c */
|
||||
int ietf_hardware_init(struct confd *confd);
|
||||
|
||||
#endif /* CONFD_CORE_H_ */
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
#include <jansson.h>
|
||||
|
||||
#include <srx/common.h>
|
||||
#include <srx/lyx.h>
|
||||
#include <srx/srx_module.h>
|
||||
#include <srx/srx_val.h>
|
||||
#include <jansson.h>
|
||||
#include <ftw.h>
|
||||
#include <libgen.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
#define XPATH_BASE_ "/ietf-hardware:hardware"
|
||||
|
||||
static int dir_cb(const char *fpath, const struct stat *sb,
|
||||
int typeflag, struct FTW *ftwbuf)
|
||||
{
|
||||
char *filename;
|
||||
if (typeflag == FTW_DP)
|
||||
return 0;
|
||||
|
||||
filename = basename((char *)fpath);
|
||||
if (!strcmp(filename, "authorized_default") || !strcmp(filename, "authorized")) {
|
||||
if (writedf(1, "w", "%s", fpath)) {
|
||||
ERROR("Failed to authorize %s", fpath);
|
||||
return FTW_STOP;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool usb_authorize(struct json_t *root, const char *name, int enabled)
|
||||
{
|
||||
json_t *usb_port, *usb_ports;
|
||||
int index;
|
||||
|
||||
usb_ports = json_object_get(root, "usb-ports");
|
||||
if (!usb_ports) /* No Infix controlled USB ports is ok */
|
||||
return 0;
|
||||
|
||||
json_array_foreach(usb_ports, index, usb_port) {
|
||||
struct json_t *jname, *jpath;
|
||||
const char *path;
|
||||
jname = json_object_get(usb_port, "name");
|
||||
if (!jname || !json_is_string(jname)) {
|
||||
ERROR("Did not find USB hardware port (name) for %s", name);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(name, json_string_value(jname))) {
|
||||
jpath = json_object_get(usb_port, "path");
|
||||
if (!jpath || !json_is_string(jpath)) {
|
||||
ERROR("Did not find USB hardware port (path) for %s", name);
|
||||
continue;
|
||||
}
|
||||
path = json_string_value(jpath);
|
||||
if (!enabled) {
|
||||
if (fexist(path)) {
|
||||
if (writedf(0, "w", "%s", path)) {
|
||||
ERROR("Failed to unauthorize %s", path);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
char *rpath, *path_dup, *dir;
|
||||
path_dup = strdup(path);
|
||||
if (!path_dup) {
|
||||
ERROR("Failed to allocate memory.");
|
||||
return 1;
|
||||
}
|
||||
dir = dirname((char *)path_dup);
|
||||
rpath = realpath(dir, NULL);
|
||||
if (rpath) {
|
||||
nftw(rpath, dir_cb, 0, FTW_DEPTH | FTW_PHYS);
|
||||
free(rpath);
|
||||
}
|
||||
free(path_dup);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *component_xpath(const char *xpath)
|
||||
{
|
||||
char *path, *ptr;
|
||||
|
||||
if (!xpath)
|
||||
return NULL;
|
||||
|
||||
path = strdup(xpath);
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
if (!(ptr = strstr(path, "]/"))) {
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
ptr[1] = 0;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static int hardware_cand_infer_class(json_t *root, sr_session_ctx_t *session, const char *path)
|
||||
{
|
||||
sr_val_t inferred = { .type = SR_STRING_T };
|
||||
struct json_t *usb_ports, *usb_port;
|
||||
sr_error_t err = SR_ERR_OK;
|
||||
char *name, *class;
|
||||
char *xpath;
|
||||
int index;
|
||||
|
||||
xpath = component_xpath(path);
|
||||
if (!xpath)
|
||||
return SR_ERR_SYS;
|
||||
|
||||
class = srx_get_str(session, "%s/class", xpath);
|
||||
if (class) {
|
||||
free(class);
|
||||
goto out_free_xpath;
|
||||
}
|
||||
|
||||
name = srx_get_str(session, "%s/name", xpath);
|
||||
if (!name) {
|
||||
err = SR_ERR_INTERNAL;
|
||||
goto out_free_xpath;
|
||||
}
|
||||
usb_ports = json_object_get(root, "usb-ports");
|
||||
if (!usb_ports)
|
||||
goto out_free_name; /* No USB-ports is OK */
|
||||
|
||||
json_array_foreach(usb_ports, index, usb_port) {
|
||||
struct json_t *n = json_object_get(usb_port, "name");
|
||||
if (!n || !json_is_string(n)) {
|
||||
ERROR("Did not find hardware port for %s", name);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(name, json_string_value(n))) {
|
||||
inferred.data.string_val = "infix-hardware:usb";
|
||||
err = srx_set_item(session, &inferred, 0,
|
||||
"%s/class", xpath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
out_free_name:
|
||||
free(name);
|
||||
out_free_xpath:
|
||||
free(xpath);
|
||||
return err;
|
||||
}
|
||||
static int hardware_cand(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
|
||||
{
|
||||
sr_change_iter_t *iter;
|
||||
sr_change_oper_t op;
|
||||
sr_val_t *old, *new;
|
||||
sr_error_t err = SR_ERR_OK;
|
||||
struct confd *confd = (struct confd *)priv;
|
||||
|
||||
switch (event) {
|
||||
case SR_EV_UPDATE:
|
||||
case SR_EV_CHANGE:
|
||||
break;
|
||||
default:
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
err = sr_dup_changes_iter(session, "/ietf-hardware:hardware/component//*", &iter);
|
||||
if (err)
|
||||
return err;
|
||||
while (sr_get_change_next(session, iter, &op, &old, &new) == SR_ERR_OK) {
|
||||
switch (op) {
|
||||
case SR_OP_CREATED:
|
||||
case SR_OP_MODIFIED:
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
err = hardware_cand_infer_class(confd->root, session, new->xpath);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
sr_free_change_iter(iter);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int change_hardware(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
|
||||
{
|
||||
struct lyd_node *diff, *difs = NULL, *dif = NULL, *cifs = NULL, *cif = NULL;
|
||||
sr_data_t *cfg;
|
||||
int rc = SR_ERR_OK;
|
||||
struct confd *confd = (struct confd *)priv;
|
||||
|
||||
if (event != SR_EV_DONE)
|
||||
return SR_ERR_OK;
|
||||
|
||||
rc = sr_get_data(session, XPATH_BASE_ "//.", 0, 0, 0, &cfg);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
rc = srx_get_diff(session, &diff);
|
||||
if (rc)
|
||||
goto err_release_data;
|
||||
|
||||
cifs = lydx_get_descendant(cfg->tree, "hardware", "component", NULL);
|
||||
difs = lydx_get_descendant(diff, "hardware", "component", NULL);
|
||||
|
||||
LYX_LIST_FOR_EACH(difs, dif, "component") {
|
||||
enum lydx_op op;
|
||||
struct lyd_node *state;
|
||||
const char *admin_state;
|
||||
const char *class, *name;
|
||||
|
||||
op = lydx_get_op(dif);
|
||||
name = lydx_get_cattr(dif, "name");
|
||||
if (op == LYDX_OP_DELETE) {
|
||||
if (usb_authorize(confd->root, name, 0)) {
|
||||
rc = SR_ERR_INTERNAL;
|
||||
goto err_release_diff;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
LYX_LIST_FOR_EACH(cifs, cif, "component") {
|
||||
if (strcmp(name, lydx_get_cattr(cif, "name")))
|
||||
continue;
|
||||
|
||||
class = lydx_get_cattr(cif, "class");
|
||||
if (strcmp(class, "infix-hardware:usb")) {
|
||||
continue;
|
||||
}
|
||||
state = lydx_get_child(dif, "state");
|
||||
admin_state = lydx_get_cattr(state, "admin-state");
|
||||
if (usb_authorize(confd->root, name, !strcmp(admin_state, "unlocked"))) {
|
||||
rc = SR_ERR_INTERNAL;
|
||||
goto err_release_diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err_release_diff:
|
||||
lyd_free_tree(diff);
|
||||
err_release_data:
|
||||
sr_release_data(cfg);
|
||||
err:
|
||||
return rc;
|
||||
}
|
||||
int ietf_hardware_init(struct confd *confd)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
REGISTER_CHANGE(confd->session, "ietf-hardware", XPATH_BASE_, 0, change_hardware, confd, &confd->sub);
|
||||
REGISTER_CHANGE(confd->cand, "ietf-hardware", XPATH_BASE_,
|
||||
SR_SUBSCR_UPDATE, hardware_cand, confd, &confd->sub);
|
||||
|
||||
return SR_ERR_OK;
|
||||
fail:
|
||||
ERROR("Init hardware failed: %s", sr_strerror(rc));
|
||||
return rc;
|
||||
}
|
||||
+141
-44
@@ -12,7 +12,7 @@
|
||||
#include <srx/srx_val.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "dagger.h"
|
||||
#include "cni.h"
|
||||
|
||||
#define ERR_IFACE(_iface, _err, _fmt, ...) \
|
||||
({ \
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#define IF_XPATH "/ietf-interfaces:interfaces/interface"
|
||||
|
||||
|
||||
static bool iface_is_phys(const char *ifname)
|
||||
{
|
||||
bool is_phys = false;
|
||||
@@ -34,7 +35,7 @@ static bool iface_is_phys(const char *ifname)
|
||||
json_t *link;
|
||||
FILE *proc;
|
||||
|
||||
proc = popenf("re", "ip -d -j link show dev %s 2>/dev/null", ifname);
|
||||
proc = cni_popen("ip -d -j link show dev %s 2>/dev/null", ifname);
|
||||
if (!proc)
|
||||
goto out;
|
||||
|
||||
@@ -61,37 +62,13 @@ out:
|
||||
return is_phys;
|
||||
}
|
||||
|
||||
/*
|
||||
* Needed because we do deep searches for changes in interfaces,
|
||||
* e.g. changes in bridge port settings, veth peers, etc.
|
||||
*/
|
||||
static char *iface_xpath(const char *xpath)
|
||||
{
|
||||
char *path, *ptr;
|
||||
|
||||
if (!xpath)
|
||||
return NULL;
|
||||
|
||||
path = strdup(xpath);
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
if (!(ptr = strstr(path, "]/"))) {
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
ptr[1] = 0;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static int ifchange_cand_infer_veth(sr_session_ctx_t *session, const char *path)
|
||||
{
|
||||
char *ifname, *type, *peer, *xpath, *val;
|
||||
sr_error_t err = SR_ERR_OK;
|
||||
size_t cnt = 0;
|
||||
|
||||
xpath = iface_xpath(path);
|
||||
xpath = xpath_base(path);
|
||||
if (!xpath)
|
||||
return SR_ERR_SYS;
|
||||
|
||||
@@ -141,7 +118,7 @@ static int ifchange_cand_infer_vlan(sr_session_ctx_t *session, const char *path)
|
||||
size_t cnt = 0;
|
||||
long vid;
|
||||
|
||||
xpath = iface_xpath(path);
|
||||
xpath = xpath_base(path);
|
||||
if (!xpath)
|
||||
return SR_ERR_SYS;;
|
||||
type = srx_get_str(session, "%s/type", xpath);
|
||||
@@ -222,7 +199,7 @@ static int ifchange_cand_infer_type(sr_session_ctx_t *session, const char *path)
|
||||
char *ifname, *type, *xpath;
|
||||
sr_error_t err = SR_ERR_OK;
|
||||
|
||||
xpath = iface_xpath(path);
|
||||
xpath = xpath_base(path);
|
||||
if (!xpath)
|
||||
return SR_ERR_SYS;
|
||||
|
||||
@@ -242,6 +219,10 @@ static int ifchange_cand_infer_type(sr_session_ctx_t *session, const char *path)
|
||||
inferred.data.string_val = "infix-if-type:ethernet";
|
||||
else if (!fnmatch("br+([0-9])", ifname, FNM_EXTMATCH))
|
||||
inferred.data.string_val = "infix-if-type:bridge";
|
||||
else if (!fnmatch("docker+([0-9])", ifname, FNM_EXTMATCH))
|
||||
inferred.data.string_val = "infix-if-type:bridge";
|
||||
else if (!fnmatch("podman+([0-9])", ifname, FNM_EXTMATCH))
|
||||
inferred.data.string_val = "infix-if-type:bridge";
|
||||
else if (!fnmatch("lag+([0-9])", ifname, FNM_EXTMATCH))
|
||||
inferred.data.string_val = "infix-if-type:lag";
|
||||
else if (!fnmatch("veth+([0-9a-z_-])", ifname, FNM_EXTMATCH))
|
||||
@@ -301,6 +282,10 @@ static int ifchange_cand(sr_session_ctx_t *session, uint32_t sub_id, const char
|
||||
err = ifchange_cand_infer_vlan(session, new->xpath);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
err = cni_ifchange_cand_infer_type(session, new->xpath);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
sr_free_change_iter(iter);
|
||||
@@ -445,7 +430,7 @@ static int netdag_gen_link_addr(FILE *ip, struct lyd_node *cif, struct lyd_node
|
||||
* Only physical interfaces support this, virtual ones
|
||||
* we remove, see netdag_must_del() for details.
|
||||
*/
|
||||
fp = popenf("r", "ip -d -j link show dev %s |jq -rM .[].permaddr", ifname);
|
||||
fp = cni_popen("ip -d -j link show dev %s |jq -rM .[].permaddr", ifname);
|
||||
if (fp) {
|
||||
if (fgets(buf, sizeof(buf), fp))
|
||||
mac = chomp(buf);
|
||||
@@ -467,7 +452,7 @@ static int netdag_gen_link_addr(FILE *ip, struct lyd_node *cif, struct lyd_node
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdag_gen_ip_addrs(FILE *ip, const char *proto,
|
||||
static int netdag_gen_ip_addrs(struct dagger *net, FILE *ip, const char *proto,
|
||||
struct lyd_node *cif, struct lyd_node *dif)
|
||||
{
|
||||
struct lyd_node *ipconf = lydx_get_child(cif, proto);
|
||||
@@ -475,8 +460,15 @@ static int netdag_gen_ip_addrs(FILE *ip, const char *proto,
|
||||
const char *ifname = lydx_get_cattr(dif, "name");
|
||||
|
||||
if (!ipconf || !lydx_is_enabled(ipconf, "enabled")) {
|
||||
if (if_nametoindex(ifname))
|
||||
systemf("ip -%c addr flush dev %s\n", proto[3], ifname);
|
||||
if (!cni_find(ifname) && if_nametoindex(ifname)) {
|
||||
FILE *fp;
|
||||
|
||||
fp = dagger_fopen_next(net, "init", ifname, 49, "flush.sh");
|
||||
if (fp) {
|
||||
fprintf(fp, "ip -%c addr flush dev %s\n", proto[3], ifname);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -653,6 +645,94 @@ skip_mtu:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int netdag_gen_ethtool_autoneg(struct dagger *net, struct lyd_node *cif)
|
||||
{
|
||||
struct lyd_node *eth = lydx_get_child(cif, "ethernet");
|
||||
struct lyd_node *aneg = lydx_get_child(eth, "auto-negotiation");
|
||||
const char *ifname = lydx_get_cattr(cif, "name");
|
||||
const char *speed, *duplex;
|
||||
int mbps, err = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = dagger_fopen_next(net, "init", ifname, 10, "ethtool-aneg.sh");
|
||||
if (!fp)
|
||||
return -EIO;
|
||||
|
||||
fprintf(fp, "ethtool --change %s autoneg ", ifname);
|
||||
|
||||
if (!aneg || lydx_is_enabled(aneg, "enable")) {
|
||||
fputs("on\n", fp);
|
||||
} else {
|
||||
speed = lydx_get_cattr(eth, "speed");
|
||||
if (!speed) {
|
||||
sr_session_set_error_message(net->session, "%s: "
|
||||
"\"speed\" must be specified "
|
||||
"when auto-negotiation is disabled", ifname);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mbps = (int)(atof(speed) * 1000.);
|
||||
if (!((mbps == 10) || (mbps == 100))) {
|
||||
sr_session_set_error_message(net->session, "%s: "
|
||||
"\"speed\" must be either 0.01 or 0.1 "
|
||||
"when auto-negotiation is disabled", ifname);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
duplex = lydx_get_cattr(eth, "duplex");
|
||||
if (!duplex || (strcmp(duplex, "full") && strcmp(duplex, "half"))) {
|
||||
sr_session_set_error_message(net->session, "%s: "
|
||||
"\"duplex\" must be either "
|
||||
"\"full\" or \"half\" "
|
||||
"when auto-negotiation is disabled", ifname);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fprintf(fp,"off speed %d duplex %s\n", mbps, duplex);
|
||||
}
|
||||
out:
|
||||
fclose(fp);
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
static int netdag_gen_ethtool(struct dagger *net, struct lyd_node *cif, struct lyd_node *dif)
|
||||
{
|
||||
struct lyd_node *eth = lydx_get_child(dif, "ethernet");
|
||||
const char *type = lydx_get_cattr(cif, "type");
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Story time: when assigning a physical interface to a container, and then
|
||||
* removing it, even though our type may be 'etherlike' we will
|
||||
* get the following from sysrepo:
|
||||
*
|
||||
* "ieee802-ethernet-interface:ethernet": {
|
||||
* "@": {
|
||||
* "yang:operation": "delete"
|
||||
* },
|
||||
* "duplex": "full"
|
||||
* },
|
||||
*
|
||||
* Hence this "redundant" check.
|
||||
*/
|
||||
if (strcmp(type, "infix-if-type:ethernet"))
|
||||
return 0;
|
||||
|
||||
if (lydx_get_descendant(lyd_child(eth), "auto-negotiation", "enable", NULL) ||
|
||||
lydx_get_child(eth, "speed") ||
|
||||
lydx_get_child(eth, "duplex")) {
|
||||
err = netdag_gen_ethtool_autoneg(net, cif);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bridge_diff_vlan_port(struct dagger *net, FILE *br, const char *brname, int vid,
|
||||
const char *brport, int tagged, enum lydx_op op)
|
||||
{
|
||||
@@ -815,7 +895,14 @@ static int netdag_gen_bridge(struct dagger *net, struct lyd_node *dif,
|
||||
mcast_snooping = 0; /* Not supported yet */
|
||||
fwd_mask = bridge_fwd_mask(cif);
|
||||
|
||||
fprintf(ip, "link %s dev %s type bridge group_fwd_mask %d vlan_filtering %d mcast_snooping %d",
|
||||
/*
|
||||
* Issue #198: we require explicit VLAN assignment for ports
|
||||
* when VLAN filtering is enabled. We strongly
|
||||
* believe this is the only sane way of doing it.
|
||||
* Issue #310: malplaced 'vlan_default_pvid 0'
|
||||
*/
|
||||
fprintf(ip, "link %s dev %s type bridge group_fwd_mask %d vlan_filtering %d"
|
||||
" vlan_default_pvid 0 mcast_snooping %d",
|
||||
op, brname, fwd_mask, vlan_filtering ? 1 : 0, mcast_snooping ? 1 : 0);
|
||||
if (!vlan_filtering) {
|
||||
fputc('\n', ip);
|
||||
@@ -827,12 +914,7 @@ static int netdag_gen_bridge(struct dagger *net, struct lyd_node *dif,
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Issue #198: we require explicit VLAN assignment for ports
|
||||
* when VLAN filtering is enabled. We strongly
|
||||
* believe this is the only sane way of doing it.
|
||||
*/
|
||||
fprintf(ip, " vlan_protocol %s vlan_default_pvid 0\n", proto);
|
||||
fprintf(ip, " vlan_protocol %s\n", proto);
|
||||
|
||||
vlans = lydx_get_descendant(lyd_child(dif), "bridge", "vlans", NULL);
|
||||
if (!vlans)
|
||||
@@ -970,6 +1052,7 @@ static int netdag_gen_afspec_add(struct dagger *net, struct lyd_node *dif,
|
||||
static int netdag_gen_afspec_set(struct dagger *net, struct lyd_node *dif,
|
||||
struct lyd_node *cif, FILE *ip)
|
||||
{
|
||||
const char *ifname = lydx_get_cattr(cif, "name");
|
||||
const char *iftype = lydx_get_cattr(cif, "type");
|
||||
|
||||
DEBUG_IFACE(dif, "");
|
||||
@@ -981,7 +1064,7 @@ static int netdag_gen_afspec_set(struct dagger *net, struct lyd_node *dif,
|
||||
if (!strcmp(iftype, "infix-if-type:veth"))
|
||||
return 0;
|
||||
|
||||
ERROR("unsupported interface type \"%s\"", iftype);
|
||||
ERROR("unsupported interface type \"%s\" for %s", iftype, ifname);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
@@ -1000,7 +1083,8 @@ static bool netdag_must_del(struct lyd_node *dif, struct lyd_node *cif)
|
||||
{
|
||||
const char *iftype = lydx_get_cattr(cif, "type");
|
||||
|
||||
if (strcmp(iftype, "infix-if-type:ethernet")) {
|
||||
if (strcmp(iftype, "infix-if-type:ethernet") &&
|
||||
strcmp(iftype, "infix-if-type:etherlike")) {
|
||||
if (is_phys_addr_deleted(dif))
|
||||
return true;
|
||||
}
|
||||
@@ -1059,6 +1143,13 @@ static sr_error_t netdag_gen_iface(struct dagger *net,
|
||||
bool fixed;
|
||||
FILE *ip;
|
||||
|
||||
if ((err = cni_netdag_gen_iface(net, ifname, dif, cif))) {
|
||||
/* error or managed by CNI/podman */
|
||||
if (err > 0)
|
||||
err = 0; /* done, nothing more to do here */
|
||||
goto err;
|
||||
}
|
||||
|
||||
fixed = iface_is_phys(ifname) || !strcmp(ifname, "lo");
|
||||
|
||||
DEBUG("%s(%s) %s", ifname, fixed ? "fixed" : "dynamic",
|
||||
@@ -1128,8 +1219,8 @@ static sr_error_t netdag_gen_iface(struct dagger *net,
|
||||
/* Set Addresses */
|
||||
err = err ? : netdag_gen_link_mtu(ip, dif);
|
||||
err = err ? : netdag_gen_link_addr(ip, cif, dif);
|
||||
err = err ? : netdag_gen_ip_addrs(ip, "ipv4", cif, dif);
|
||||
err = err ? : netdag_gen_ip_addrs(ip, "ipv6", cif, dif);
|
||||
err = err ? : netdag_gen_ip_addrs(net, ip, "ipv4", cif, dif);
|
||||
err = err ? : netdag_gen_ip_addrs(net, ip, "ipv6", cif, dif);
|
||||
if (err)
|
||||
goto err_close_ip;
|
||||
|
||||
@@ -1140,6 +1231,8 @@ static sr_error_t netdag_gen_iface(struct dagger *net,
|
||||
|
||||
err = err ? : netdag_gen_sysctl(net, cif, dif);
|
||||
|
||||
err = err ? : netdag_gen_ethtool(net, cif, dif);
|
||||
|
||||
err_close_ip:
|
||||
fclose(ip);
|
||||
err:
|
||||
@@ -1230,6 +1323,10 @@ int ietf_interfaces_init(struct confd *confd)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
#ifdef CONTAINERS
|
||||
sr_enable_module_feature(confd->conn, "infix-interfaces", "containers");
|
||||
#endif
|
||||
|
||||
REGISTER_CHANGE(confd->session, "ietf-interfaces", "/ietf-interfaces:interfaces//.",
|
||||
0, ifchange, confd, &confd->sub);
|
||||
REGISTER_CHANGE(confd->cand, "ietf-interfaces", "/ietf-interfaces:interfaces//.",
|
||||
|
||||
@@ -145,7 +145,7 @@ int parse_ospf(sr_session_ctx_t *session, struct lyd_node *ospf)
|
||||
return 0;
|
||||
}
|
||||
if (bfd_enabled)
|
||||
touch(BFDD_CONF);
|
||||
(void)touch(BFDD_CONF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <srx/srx_module.h>
|
||||
#include <srx/srx_val.h>
|
||||
|
||||
#include "base64.h"
|
||||
#include "core.h"
|
||||
|
||||
#define XPATH_BASE_ "/ietf-system:system"
|
||||
@@ -479,8 +480,10 @@ static int change_ntp(sr_session_ctx_t *session, uint32_t sub_id, const char *mo
|
||||
}
|
||||
sr_free_values(val, cnt);
|
||||
|
||||
if (changes)
|
||||
touch("/run/chrony/.changes");
|
||||
if (changes) {
|
||||
if (touch("/run/chrony/.changes"))
|
||||
ERRNO("Failed recording changes to NTP client");
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -1026,18 +1029,105 @@ static int change_motd(sr_session_ctx_t *session, uint32_t sub_id, const char *m
|
||||
if (message) {
|
||||
rc = writesf(message, "w", "%s", fn);
|
||||
free(message);
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
ERRNO("failed saving %s", fn);
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static int change_motd_banner(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
|
||||
{
|
||||
const char *fn = "/etc/motd";
|
||||
unsigned char *raw, *txt;
|
||||
size_t txt_len;
|
||||
char *legacy;
|
||||
int rc = 0;
|
||||
|
||||
/* Ignore all events except SR_EV_DONE */
|
||||
if (event != SR_EV_DONE)
|
||||
return SR_ERR_OK;
|
||||
|
||||
legacy = srx_get_str(session, "/ietf-system:system/infix-sys:motd");
|
||||
if (legacy) {
|
||||
NOTE("Legacy /system/motd exists, skipping %s", xpath);
|
||||
free(legacy);
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
raw = (unsigned char *)srx_get_str(session, "%s", xpath);
|
||||
if (raw) {
|
||||
txt = base64_decode(raw, strlen((char *)raw), &txt_len);
|
||||
if (!txt) {
|
||||
ERRNO("failed base64 decoding of %s", xpath);
|
||||
rc = -1;
|
||||
} else {
|
||||
FILE *fp = fopen(fn, "w");
|
||||
|
||||
if (!fp) {
|
||||
rc = -1;
|
||||
} else {
|
||||
if (fwrite(txt, txt_len, sizeof(txt[0]), fp) != txt_len)
|
||||
rc = -1;
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
free(txt);
|
||||
}
|
||||
|
||||
free(raw);
|
||||
} else {
|
||||
(void)remove(fn);
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
ERROR("failed writing /etc/motd: %s", strerror(errno));
|
||||
ERRNO("failed saving %s", fn);
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static int change_editor(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
|
||||
{
|
||||
const char *alt = "/etc/alternatives/editor";
|
||||
struct { const char *editor, *path; } map[] = {
|
||||
{ "emacs", "/usr/bin/mg" },
|
||||
{ "nano", "/usr/bin/nano" },
|
||||
{ "vi", "/bin/vi" },
|
||||
};
|
||||
char *editor;
|
||||
int rc = 0;
|
||||
|
||||
/* Ignore all events except SR_EV_DONE */
|
||||
if (event != SR_EV_DONE)
|
||||
return SR_ERR_OK;
|
||||
|
||||
editor = srx_get_str(session, "%s", xpath);
|
||||
if (editor) {
|
||||
for (size_t i = 0; i < NELEMS(map); i++) {
|
||||
if (strcmp(map[i].editor, editor))
|
||||
continue;
|
||||
|
||||
erase(alt);
|
||||
rc = systemf("ln -s %s %s", map[i].path, alt);
|
||||
if (rc)
|
||||
ERROR("Failed setting system editor '%s'", map[i].editor);
|
||||
}
|
||||
free(editor);
|
||||
}
|
||||
|
||||
if (rc)
|
||||
return SR_ERR_SYS;
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static int change_hostname(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
|
||||
{
|
||||
@@ -1135,6 +1225,8 @@ int ietf_system_init(struct confd *confd)
|
||||
REGISTER_CHANGE(confd->session, "ietf-system", XPATH_AUTH_, 0, change_auth, confd, &confd->sub);
|
||||
REGISTER_CHANGE(confd->session, "ietf-system", XPATH_BASE_"/hostname", 0, change_hostname, confd, &confd->sub);
|
||||
REGISTER_CHANGE(confd->session, "ietf-system", XPATH_BASE_"/infix-system:motd", 0, change_motd, confd, &confd->sub);
|
||||
REGISTER_CHANGE(confd->session, "ietf-system", XPATH_BASE_"/infix-system:motd-banner", 0, change_motd_banner, confd, &confd->sub);
|
||||
REGISTER_CHANGE(confd->session, "ietf-system", XPATH_BASE_"/infix-system:text-editor", 0, change_editor, confd, &confd->sub);
|
||||
REGISTER_CHANGE(confd->session, "ietf-system", XPATH_BASE_"/clock", 0, change_clock, confd, &confd->sub);
|
||||
REGISTER_CHANGE(confd->session, "ietf-system", XPATH_BASE_"/ntp", 0, change_ntp, confd, &confd->sub);
|
||||
REGISTER_CHANGE(confd->session, "ietf-system", XPATH_BASE_"/dns-resolver", 0, change_dns, confd, &confd->sub);
|
||||
|
||||
@@ -0,0 +1,447 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <srx/common.h>
|
||||
#include <srx/lyx.h>
|
||||
#include <srx/srx_module.h>
|
||||
#include <srx/srx_val.h>
|
||||
|
||||
#include "core.h"
|
||||
#define ARPING_MSEC 1000
|
||||
#define MODULE "infix-containers"
|
||||
#define CFG_XPATH "/infix-containers:containers"
|
||||
#define INBOX_QUEUE "/run/containers/inbox"
|
||||
#define JOB_QUEUE "/run/containers/queue"
|
||||
#define ACTIVE_QUEUE "/var/lib/containers/active"
|
||||
#define LOGGER "logger -t container -p local1.notice"
|
||||
|
||||
static const struct srx_module_requirement reqs[] = {
|
||||
{ .dir = YANG_PATH_, .name = MODULE, .rev = "2024-02-01" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static int add(const char *name, struct lyd_node *cif)
|
||||
{
|
||||
const char *image = lydx_get_cattr(cif, "image");
|
||||
const char *restart_policy, *string;
|
||||
struct lyd_node *node, *network;
|
||||
FILE *fp, *ap;
|
||||
|
||||
fp = fopenf("w", "%s/%s.sh", INBOX_QUEUE, name);
|
||||
if (!fp) {
|
||||
ERRNO("Failed adding job %s.sh to job queue" INBOX_QUEUE, name);
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
/* Stop any running container gracefully so it releases its IP addresses. */
|
||||
fprintf(fp, "#!/bin/sh\n"
|
||||
"container stop %s\n"
|
||||
"container delete %s\n"
|
||||
"container", name, name);
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(cif), node, "dns")
|
||||
fprintf(fp, " --dns %s", lyd_get_value(node));
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(cif), node, "search")
|
||||
fprintf(fp, " --dns-search %s", lyd_get_value(node));
|
||||
|
||||
if ((string = lydx_get_cattr(cif, "hostname")))
|
||||
fprintf(fp, " --hostname %s", string);
|
||||
|
||||
if (lydx_is_enabled(cif, "read-only"))
|
||||
fprintf(fp, " --read-only");
|
||||
|
||||
if (lydx_is_enabled(cif, "privileged"))
|
||||
fprintf(fp, " --privileged");
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(cif), node, "volume")
|
||||
fprintf(fp, " -v %s-%s:%s", name, lydx_get_cattr(node, "name"),
|
||||
lydx_get_cattr(node, "target"));
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(cif), node, "mount") {
|
||||
const char *dst = lydx_get_cattr(node, "target");
|
||||
const char *data = lydx_get_cattr(node, "content");
|
||||
const char *src = lydx_get_cattr(node, "source");
|
||||
const char *type = lydx_get_cattr(node, "type");
|
||||
const char *id = lydx_get_cattr(node, "name");
|
||||
int ro = lydx_get_bool(node, "read-only");
|
||||
char nm[strlen(id) + strlen(name) + 32];
|
||||
|
||||
/* Content mount: create a unique file with 'content' and bind mount */
|
||||
if (data) {
|
||||
const char *contdir = "/run/containers/files";
|
||||
char cmd[256];
|
||||
FILE *pp;
|
||||
int pos;
|
||||
|
||||
/*
|
||||
* prefix file name with container name, shared namespace,
|
||||
* and replace any slashes with hyphens.
|
||||
*/
|
||||
pos = snprintf(nm, sizeof(nm), "%s/%s-", contdir, name);
|
||||
strlcat(nm, id, sizeof(nm));
|
||||
for (int i = pos; nm[i] && i < (int)sizeof(nm); i++) {
|
||||
if (nm[i] == '/')
|
||||
nm[i] = '-';
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "base64 -d > %s", nm);
|
||||
pp = popen(cmd, "w");
|
||||
if (!pp || fputs(data, pp) < 0) {
|
||||
ERRNO("%s: failed decoding %s base64 'content'", name, id);
|
||||
if (pp)
|
||||
pclose(pp);
|
||||
continue;
|
||||
}
|
||||
|
||||
pclose(pp);
|
||||
type = "bind"; /* discard any configured setting */
|
||||
src = nm; /* discard any source, not used for content mounts */
|
||||
}
|
||||
|
||||
fprintf(fp, " -m type=%s,src=%s,dst=%s,readonly=%s",
|
||||
type, src, dst, ro ? "true" : "false");
|
||||
}
|
||||
|
||||
ap = NULL;
|
||||
LYX_LIST_FOR_EACH(lyd_child(cif), node, "env") {
|
||||
if (!ap) {
|
||||
ap = fopenf("w", "/run/containers/args/%s.env", name);
|
||||
if (!ap) {
|
||||
ERRNO("Failed creating env list for container %s", name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprintf(ap, "%s=%s\n", lydx_get_cattr(node, "key"), lydx_get_cattr(node, "value"));
|
||||
}
|
||||
if (ap) {
|
||||
fclose(ap);
|
||||
fprintf(fp, " -e /run/containers/args/%s.env", name);
|
||||
}
|
||||
|
||||
network = lydx_get_descendant(lyd_child(cif), "network", NULL);
|
||||
if (network) {
|
||||
if (lydx_is_enabled(network, "host")) {
|
||||
fprintf(fp, " --net host");
|
||||
} else {
|
||||
LYX_LIST_FOR_EACH(lyd_child(network), node, "interface") {
|
||||
struct lyd_node *opt;
|
||||
const char *name;
|
||||
int first = 1;
|
||||
|
||||
name = lydx_get_cattr(node, "name");
|
||||
fprintf(fp, " --net %s", name);
|
||||
LYX_LIST_FOR_EACH(lyd_child(node), opt, "option") {
|
||||
const char *option = lyd_get_value(opt);
|
||||
|
||||
fprintf(fp, "%s%s", first ? ":" : ",", option);
|
||||
first = 0;
|
||||
}
|
||||
}
|
||||
|
||||
LYX_LIST_FOR_EACH(lyd_child(network), node, "publish")
|
||||
fprintf(fp, " -p %s", lyd_get_value(node));
|
||||
}
|
||||
}
|
||||
|
||||
restart_policy = lydx_get_cattr(cif, "restart-policy");
|
||||
if (restart_policy) {
|
||||
if (!strcmp(restart_policy, "never"))
|
||||
fprintf(fp, " -r no"); /* for docker compat */
|
||||
else if (!strcmp(restart_policy, "always"))
|
||||
fprintf(fp, " -r always");
|
||||
else
|
||||
fprintf(fp, " -r on-failure:10");
|
||||
}
|
||||
|
||||
if (lydx_is_enabled(cif, "manual"))
|
||||
fprintf(fp, " --manual");
|
||||
|
||||
if ((string = lydx_get_cattr(cif, "command")))
|
||||
fprintf(fp, " --entrypoint");
|
||||
|
||||
fprintf(fp, " create %s %s", name, image);
|
||||
|
||||
if ((string = lydx_get_cattr(cif, "command")))
|
||||
fprintf(fp, " %s", string);
|
||||
|
||||
fprintf(fp, "\n");
|
||||
fchmod(fileno(fp), 0700);
|
||||
fclose(fp);
|
||||
|
||||
systemf("initctl -bnq enable container@%s.conf", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int del(const char *name)
|
||||
{
|
||||
const char *queue[] = {
|
||||
JOB_QUEUE,
|
||||
INBOX_QUEUE,
|
||||
ACTIVE_QUEUE,
|
||||
};
|
||||
|
||||
/* Remove any pending download/create job first */
|
||||
for (size_t i = 0; i < NELEMS(queue); i++) {
|
||||
char fn[strlen(queue[i]) + strlen(name) + 5];
|
||||
|
||||
snprintf(fn, sizeof(fn), "%s/%s.sh", queue[i], name);
|
||||
erase(fn);
|
||||
}
|
||||
|
||||
return systemf("container delete %s", name) ||
|
||||
systemf("initctl -bnq disable container@%s.conf", name);
|
||||
}
|
||||
|
||||
static int change(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
|
||||
const char *xpath, sr_event_t event, unsigned request_id, void *_confd)
|
||||
{
|
||||
struct lyd_node *diff, *cifs, *difs, *cif, *dif;
|
||||
sr_error_t err = 0;
|
||||
sr_data_t *cfg;
|
||||
|
||||
switch (event) {
|
||||
case SR_EV_DONE:
|
||||
break;
|
||||
case SR_EV_CHANGE:
|
||||
case SR_EV_ABORT:
|
||||
default:
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
err = sr_get_data(session, CFG_XPATH "//.", 0, 0, 0, &cfg);
|
||||
if (err)
|
||||
goto err_abandon;
|
||||
|
||||
err = srx_get_diff(session, &diff);
|
||||
if (err)
|
||||
goto err_release_data;
|
||||
|
||||
cifs = lydx_get_descendant(cfg->tree, "containers", "container", NULL);
|
||||
difs = lydx_get_descendant(diff, "containers", "container", NULL);
|
||||
|
||||
/* find the modified one, delete or recreate only that */
|
||||
LYX_LIST_FOR_EACH(difs, dif, "container") {
|
||||
const char *name = lydx_get_cattr(dif, "name");
|
||||
|
||||
if (lydx_get_op(dif) == LYDX_OP_DELETE) {
|
||||
del(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
LYX_LIST_FOR_EACH(cifs, cif, "container") {
|
||||
const char *nm = lydx_get_cattr(cif, "name");
|
||||
|
||||
if (strcmp(name, nm))
|
||||
continue;
|
||||
|
||||
if (!lydx_is_enabled(cif, "enabled"))
|
||||
del(name);
|
||||
else
|
||||
add(name, cif);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lyd_free_tree(diff);
|
||||
err_release_data:
|
||||
sr_release_data(cfg);
|
||||
err_abandon:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int action(sr_session_ctx_t *session, uint32_t sub_id, const char *xpath,
|
||||
const sr_val_t *input, const size_t input_cnt,
|
||||
sr_event_t event, unsigned request_id,
|
||||
sr_val_t **output, size_t *output_cnt,
|
||||
void *priv)
|
||||
{
|
||||
char buf[strlen(xpath) + 1];
|
||||
char *cmd, *name, *ptr;
|
||||
char quote;
|
||||
|
||||
/* /infix-containers:containers/container[name='ntpd']/restart */
|
||||
strlcpy(buf, xpath, sizeof(buf));
|
||||
|
||||
name = strstr(buf, "[name=");
|
||||
if (!name)
|
||||
return SR_ERR_INTERNAL;
|
||||
|
||||
ptr = &name[6];
|
||||
quote = ptr[0]; /* Quote char to look for: ' or " */
|
||||
name = &ptr[1];
|
||||
|
||||
ptr = strchr(name, quote);
|
||||
if (!ptr)
|
||||
return SR_ERR_INTERNAL;
|
||||
*ptr++ = 0;
|
||||
|
||||
cmd = strstr(ptr, "]/");
|
||||
if (!cmd)
|
||||
return SR_ERR_INTERNAL;
|
||||
cmd += 2;
|
||||
|
||||
DEBUG("CALLING 'container %s %s' (xpath %s)", cmd, name, xpath);
|
||||
if (systemf("container %s %s", cmd, name))
|
||||
return SR_ERR_INTERNAL;
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static int oci_load(sr_session_ctx_t *session, uint32_t sub_id, const char *xpath,
|
||||
const sr_val_t *input, const size_t input_cnt, sr_event_t event,
|
||||
unsigned request_id, sr_val_t **output, size_t *output_cnt, void *priv)
|
||||
{
|
||||
char *uri, *name = "";
|
||||
|
||||
uri = input[0].data.string_val; /* mandatory */
|
||||
if (input_cnt > 1)
|
||||
name = input[1].data.string_val;
|
||||
|
||||
if (systemf("container load %s %s 2>&1 | logger -t confd -p local1.err -I $PPID", uri, name))
|
||||
return SR_ERR_SYS;
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static int is_active(sr_session_ctx_t *session, const char *name)
|
||||
{
|
||||
return srx_enabled(session, CFG_XPATH "/container[name='%s']/enabled", name);
|
||||
}
|
||||
|
||||
static int is_manual(sr_session_ctx_t *session, const char *name)
|
||||
{
|
||||
return srx_enabled(session, CFG_XPATH "/container[name='%s']/manual", name);
|
||||
}
|
||||
|
||||
/*
|
||||
* When container configurations are not saved to startup-config and the
|
||||
* user reboot the system (or lose power) we will have lingering active
|
||||
* containers cached on persistent storage.
|
||||
*
|
||||
* This function runs every time a configuration is applied to clean up
|
||||
* any lingering active jobs to prevent false matches in the cmp magic
|
||||
* in the below post-hook.
|
||||
*/
|
||||
static void cleanup(sr_session_ctx_t *session, struct confd *confd)
|
||||
{
|
||||
struct dirent *d;
|
||||
DIR *dir;
|
||||
|
||||
dir = opendir(ACTIVE_QUEUE);
|
||||
if (!dir)
|
||||
return;
|
||||
|
||||
while ((d = readdir(dir))) {
|
||||
char name[strlen(ACTIVE_QUEUE) + strlen(d->d_name) + 2];
|
||||
char *ptr;
|
||||
|
||||
if (d->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
strlcpy(name, d->d_name, sizeof(name));
|
||||
ptr = strstr(name, ".sh");
|
||||
if (!ptr)
|
||||
continue; /* odd, non-script file? */
|
||||
*ptr = 0;
|
||||
|
||||
if (is_active(session, name))
|
||||
continue;
|
||||
|
||||
/* Not found in running-config, remove stale cache. */
|
||||
snprintf(name, sizeof(name), "%s/%s", ACTIVE_QUEUE, d->d_name);
|
||||
if (erase(name))
|
||||
ERRNO("Failed removing stale container job %s", name);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
/*
|
||||
* Containers depend on a lot of other system resources being properly
|
||||
* set up, e.g., networking, which is run by dagger. So we need to wait
|
||||
* for all that before we can launch new, or modified, containers. The
|
||||
* latter is the tricky part.
|
||||
*
|
||||
* By default, containers get a writable layer which is preserved across
|
||||
* restarts/reboots of container or host -- provided we don't recreate
|
||||
* them on a reboot. Hence the cmp magic below: we check if the command
|
||||
* to create a container is the same as what is already activated, if it
|
||||
* is already activated we know 'podman create' has done its thing and
|
||||
* we can safely start the container.
|
||||
*/
|
||||
void infix_containers_post_hook(sr_session_ctx_t *session, struct confd *confd)
|
||||
{
|
||||
struct dirent *d;
|
||||
DIR *dir;
|
||||
|
||||
cleanup(session, confd);
|
||||
|
||||
dir = opendir(INBOX_QUEUE);
|
||||
if (!dir) {
|
||||
ERROR("Cannot open %s to launch scripts.", INBOX_QUEUE);
|
||||
return;
|
||||
}
|
||||
|
||||
while ((d = readdir(dir))) {
|
||||
char curr[strlen(ACTIVE_QUEUE) + strlen(d->d_name) + 2];
|
||||
char next[strlen(INBOX_QUEUE) + strlen(d->d_name) + 2];
|
||||
|
||||
if (d->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
snprintf(curr, sizeof(curr), "%s/%s", ACTIVE_QUEUE, d->d_name);
|
||||
snprintf(next, sizeof(next), "%s/%s", INBOX_QUEUE, d->d_name);
|
||||
if (!systemf("cmp %s %s >/dev/null 2>&1", curr, next)) {
|
||||
char name[strlen(d->d_name) + 1];
|
||||
char *ptr;
|
||||
|
||||
strlcpy(name, d->d_name, sizeof(name));
|
||||
ptr = strstr(name, ".sh");
|
||||
if (ptr) {
|
||||
*ptr = 0;
|
||||
|
||||
/* New job is already active, no changes, skipping ... */
|
||||
if (!is_manual(session, name))
|
||||
systemf("initctl -bnq cond set container:%s", name);
|
||||
}
|
||||
remove(next);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (movefile(next, JOB_QUEUE))
|
||||
ERRNO("Failed moving %s to job queue %s", next, JOB_QUEUE);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
systemf("container volume prune -f >/dev/null 2>&1");
|
||||
}
|
||||
|
||||
int infix_containers_init(struct confd *confd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = srx_require_modules(confd->conn, reqs);
|
||||
if (rc)
|
||||
goto fail;
|
||||
|
||||
REGISTER_CHANGE(confd->session, MODULE, CFG_XPATH, 0, change, confd, &confd->sub);
|
||||
REGISTER_RPC(confd->session, CFG_XPATH "/container/start", action, NULL, &confd->sub);
|
||||
REGISTER_RPC(confd->session, CFG_XPATH "/container/stop", action, NULL, &confd->sub);
|
||||
REGISTER_RPC(confd->session, CFG_XPATH "/container/restart", action, NULL, &confd->sub);
|
||||
REGISTER_RPC(confd->session, "/infix-containers:oci-load", oci_load, NULL, &confd->sub);
|
||||
|
||||
return SR_ERR_OK;
|
||||
fail:
|
||||
ERROR("init failed: %s", sr_strerror(rc));
|
||||
return rc;
|
||||
}
|
||||
@@ -13,9 +13,10 @@
|
||||
#include <srx/srx_val.h>
|
||||
|
||||
#include "core.h"
|
||||
#define ARPING_MSEC 1000
|
||||
#define MODULE "infix-dhcp-client"
|
||||
#define XPATH "/infix-dhcp-client:dhcp-client"
|
||||
#define ARPING_MSEC 1000
|
||||
#define MODULE "infix-dhcp-client"
|
||||
#define XPATH "/infix-dhcp-client:dhcp-client"
|
||||
#define CACHE_TEMPLATE "/var/lib/misc/%s.cache"
|
||||
|
||||
static const struct srx_module_requirement reqs[] = {
|
||||
{ .dir = YANG_PATH_, .name = MODULE, .rev = "2024-01-30" },
|
||||
@@ -25,15 +26,15 @@ static const struct srx_module_requirement reqs[] = {
|
||||
|
||||
static char *ip_cache(const char *ifname, char *str, size_t len)
|
||||
{
|
||||
const char *fn = "/var/lib/misc/%s.cache";
|
||||
|
||||
struct in_addr ina;
|
||||
char buf[128];
|
||||
FILE *fp;
|
||||
|
||||
if (!fexistf(fn, ifname))
|
||||
if (!fexistf(CACHE_TEMPLATE, ifname))
|
||||
return NULL;
|
||||
|
||||
fp = fopenf("r", fn, ifname);
|
||||
fp = fopenf("r", CACHE_TEMPLATE, ifname);
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
@@ -45,7 +46,7 @@ static char *ip_cache(const char *ifname, char *str, size_t len)
|
||||
chomp(buf);
|
||||
|
||||
if (!inet_aton(buf, &ina)) {
|
||||
erasef(fn, ifname);
|
||||
erasef(CACHE_TEMPLATE, ifname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
* Infix Containers YANG module
|
||||
*/
|
||||
module infix-containers {
|
||||
yang-version 1.1;
|
||||
namespace "urn:ietf:params:xml:ns:yang:infix-containers";
|
||||
prefix infix-cont;
|
||||
|
||||
import ietf-yang-types {
|
||||
prefix yang;
|
||||
}
|
||||
|
||||
import ietf-interfaces {
|
||||
prefix if;
|
||||
}
|
||||
|
||||
import ietf-inet-types {
|
||||
prefix inet;
|
||||
}
|
||||
|
||||
import infix-interfaces {
|
||||
prefix infix-if;
|
||||
}
|
||||
|
||||
revision 2024-02-01 {
|
||||
description "Initial revision";
|
||||
reference "internal";
|
||||
}
|
||||
|
||||
/*
|
||||
* Typedefs
|
||||
*/
|
||||
|
||||
typedef mount-type {
|
||||
type enumeration {
|
||||
enum bind {
|
||||
description "Regular bind mount of host path to container.";
|
||||
value 1;
|
||||
}
|
||||
enum glob {
|
||||
description "Glob match and bind mount matching host paths to container.";
|
||||
value 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef restart-policy {
|
||||
type enumeration {
|
||||
enum never {
|
||||
description "Do not restart containers that exit/crash.";
|
||||
value 1;
|
||||
}
|
||||
enum retry {
|
||||
description "Restart containers up to 10 times before giving up.";
|
||||
value 2;
|
||||
}
|
||||
enum always {
|
||||
description "Always restart containers when they exit.";
|
||||
value 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Data Nodes
|
||||
*/
|
||||
|
||||
container containers {
|
||||
list container {
|
||||
key "name";
|
||||
|
||||
leaf enabled {
|
||||
description "Enable or disable a container configuration.";
|
||||
type boolean;
|
||||
default true;
|
||||
}
|
||||
|
||||
leaf name {
|
||||
description "Name of the container";
|
||||
type string;
|
||||
}
|
||||
|
||||
leaf id {
|
||||
description "Container ID, unique hash.";
|
||||
config false;
|
||||
type string;
|
||||
}
|
||||
|
||||
leaf image {
|
||||
description "Docker image for the container: [transport]name[:tag|@digest]
|
||||
|
||||
quay.io/username/myimage -- Pull myimage:latest
|
||||
docker://busybox -- Pull busybox:latest from Docker Hub
|
||||
docker://ghcr.io/usr/img -- Pull img:latest from GitHub packages
|
||||
dir:/media/usb/myimage:1.1 -- Use myimage v1.1 from USB media
|
||||
docker-archive:/tmp/archive -- Use archive:latest from tarball
|
||||
oci-archive:/lib/oci/archive -- Use archive:latest from OCI archive
|
||||
May be in .tar or .tar.gz format
|
||||
|
||||
Note: if a remote repository cannot be reached, the creation of the
|
||||
container will be put on a queue that retries pull every time
|
||||
there is a route change in the host's system.";
|
||||
mandatory true;
|
||||
type string;
|
||||
}
|
||||
|
||||
leaf image-id {
|
||||
description "Docker image ID, exact hash used.";
|
||||
config false;
|
||||
type string;
|
||||
}
|
||||
|
||||
list env {
|
||||
description "Set environment variables, key=\"value\" pairs.";
|
||||
key key;
|
||||
|
||||
leaf key {
|
||||
description "Single word.";
|
||||
type string {
|
||||
pattern '[a-zA-Z_][a-zA-Z0-9_]*';
|
||||
length "1..253";
|
||||
}
|
||||
}
|
||||
|
||||
leaf value {
|
||||
description "Argument to key can be a single word or quoted multiple words.";
|
||||
mandatory true;
|
||||
type string;
|
||||
}
|
||||
}
|
||||
|
||||
leaf command {
|
||||
description "Override ENTRYPOINT from image and run command + args.";
|
||||
type string;
|
||||
}
|
||||
|
||||
leaf hostname {
|
||||
description "Sets the container host name that is available inside the container.";
|
||||
type inet:domain-name;
|
||||
}
|
||||
|
||||
leaf privileged {
|
||||
description "Give container extended privileges, e.g., access to devices.";
|
||||
type boolean;
|
||||
}
|
||||
|
||||
leaf restart-policy {
|
||||
description "Restart policy to when containers exit/crash.";
|
||||
type restart-policy;
|
||||
default always;
|
||||
}
|
||||
|
||||
leaf manual {
|
||||
description "Auto-start or manual start after creation/reboot.";
|
||||
type boolean;
|
||||
}
|
||||
|
||||
container network {
|
||||
description "Select network mode: none, host, or container network interfaces.";
|
||||
|
||||
leaf host {
|
||||
description "Run in same network namespace as host, share DNS and publish all ports.";
|
||||
type boolean;
|
||||
}
|
||||
|
||||
list interface {
|
||||
description "Container network interface(s) to connect to the container.";
|
||||
key name;
|
||||
|
||||
leaf name {
|
||||
description "Container network (interface name) to connect to the container.";
|
||||
type if:interface-ref;
|
||||
must "/if:interfaces/if:interface[if:name = current()]/infix-if:container-network" {
|
||||
error-message "Container networks must be interfaces classified as container-network.";
|
||||
}
|
||||
}
|
||||
|
||||
leaf-list option {
|
||||
when "deref(../name)/../infix-if:container-network/infix-if:type = 'infix-if:bridge'";
|
||||
description "Options for a masquerading container bridges.
|
||||
|
||||
Example: ip=1.2.3.4 -- request a specific IP (IPv4 or IPv6)
|
||||
mac=00:01:02:c0:ff:ee -- set fixed MAC address in container
|
||||
interface_name=foo0 -- set interface name inside container";
|
||||
type string;
|
||||
}
|
||||
}
|
||||
|
||||
leaf-list publish {
|
||||
description "Publish container port, or a range of ports, to the host.
|
||||
|
||||
Syntax: [[ip:][hostPort]:]containerPort[/protocol]
|
||||
|
||||
Sample: 8080:80 -- forward tcp port 8080 to container port 80
|
||||
69:69/udp -- forward udp port 69 to container port 69
|
||||
127.0.0.1:8080:80 -- forward only from loopback interface";
|
||||
type string;
|
||||
}
|
||||
|
||||
leaf-list dns {
|
||||
description "Set custom DNS servers, or 'none' to use /etc/resolv.conf in image.";
|
||||
type inet:ip-address;
|
||||
}
|
||||
|
||||
leaf-list search {
|
||||
description "Set custom DNS search domains, or '.' to not set search domain.";
|
||||
type inet:domain-name;
|
||||
}
|
||||
|
||||
must "(host and not(interface)) or (not(host) and interface) or (not(host) and not(interface))" {
|
||||
error-message "Host and interfaces are mutually exclusive";
|
||||
}
|
||||
}
|
||||
|
||||
leaf read-only {
|
||||
description "Create a read-only container. Use volumes for writable directories.";
|
||||
type boolean;
|
||||
}
|
||||
|
||||
list mount {
|
||||
description "Files, content, and directories to mount inside container.";
|
||||
key name;
|
||||
|
||||
leaf name {
|
||||
description "Unique name to identify mount, e.g., 'ntpd.conf' or 'leds'.
|
||||
|
||||
Set the source path or *content* to mount in the container,
|
||||
the latter means the file contents are stored in the host's
|
||||
startup-config, base64 encoded.
|
||||
|
||||
Example source paths:
|
||||
1. /etc/ntpd.conf File name to bind mount to 'path'
|
||||
2. /sys/class/leds/ Control LEDs from a container
|
||||
3. /dev/sda* Share all matching files (type glob!)
|
||||
|
||||
For persistent writable directories, *volumes* may be a
|
||||
better fit for your container and easier to set up.";
|
||||
type string;
|
||||
}
|
||||
|
||||
leaf type {
|
||||
description "Mount type, strict bind mount or glob match.";
|
||||
type mount-type;
|
||||
default bind;
|
||||
}
|
||||
|
||||
choice data {
|
||||
case source {
|
||||
leaf source {
|
||||
description "Host path to mount in container, may be a glob.
|
||||
|
||||
When mounting files, directories (and globs) from the host,
|
||||
the source must be an absolute path.";
|
||||
type string {
|
||||
pattern '/.*';
|
||||
}
|
||||
}
|
||||
}
|
||||
case content {
|
||||
leaf content {
|
||||
description "File contents, in base64 native format (XML/JSON).
|
||||
|
||||
With this setting the source path is not used, instead
|
||||
a temporary read-only file is created on the host with
|
||||
this content and bind-mounted into the container at the
|
||||
target destination path.
|
||||
|
||||
In the CLI, use 'set content' without and argument to
|
||||
open an editor for easy copy-paste. On exit from the
|
||||
editor the contents are base64 encoded automatically.";
|
||||
type binary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
leaf target {
|
||||
description "Absolute path to target destination inside container.
|
||||
|
||||
For example, to mount file on /etc/ntpd.conf set this path and
|
||||
then use either the 'source' path to a file on the host system,
|
||||
or import the text file using the 'content' node.";
|
||||
mandatory true;
|
||||
type string {
|
||||
pattern '/.*';
|
||||
}
|
||||
}
|
||||
|
||||
leaf read-only {
|
||||
description "All mounts are read-only by default.
|
||||
Use this option to allow containers to write to files
|
||||
and directories on the host system.
|
||||
|
||||
NOTE: 'content' files can also be set read-write, but
|
||||
no changes are saved back to hosts's datastore.";
|
||||
type boolean;
|
||||
default true;
|
||||
}
|
||||
}
|
||||
|
||||
list volume {
|
||||
description "Create a writable volume that survive container upgrades.";
|
||||
key name;
|
||||
|
||||
leaf name {
|
||||
description "Single word to identify this (named) volume.
|
||||
|
||||
Volumes are retained over the lifetime of a container and
|
||||
survive both upgrading the image and configuration changes,
|
||||
which otherwise wipe the default writable layer a container
|
||||
is given.
|
||||
|
||||
Volumes combine well with 'read-only' containers, when you
|
||||
know which files/directories you want to persist.
|
||||
|
||||
Compared to a bind mount, a volume is automatically 'synced'
|
||||
with the contents of the container's file system on first
|
||||
use. Hence, upgrading the container image will not update
|
||||
the volume if the image has new/removed files at 'path'.";
|
||||
type string {
|
||||
pattern '[a-zA-Z_][a-zA-Z0-9_]*';
|
||||
length "1..64";
|
||||
}
|
||||
}
|
||||
|
||||
leaf target {
|
||||
description "Absolute path to target destination directory inside the container.";
|
||||
mandatory true;
|
||||
type string {
|
||||
pattern '/.*';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
leaf running {
|
||||
description "Status of container, running or not.";
|
||||
config false;
|
||||
type boolean;
|
||||
}
|
||||
|
||||
leaf status {
|
||||
description "Status of container, human friendly.";
|
||||
config false;
|
||||
type string;
|
||||
}
|
||||
|
||||
action start {
|
||||
description "Start a stopped container.";
|
||||
}
|
||||
|
||||
action stop {
|
||||
description "Stop a running container.";
|
||||
}
|
||||
|
||||
action restart {
|
||||
description "Restart a running, or start, a stopped container.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpc oci-load {
|
||||
description "Load an OCI archive from file or URL to an image.";
|
||||
input {
|
||||
leaf uri {
|
||||
description "The URL or local file path, e.g., /lib/oci/archive.tar.gz";
|
||||
type string;
|
||||
mandatory true;
|
||||
}
|
||||
leaf name {
|
||||
description "Image name[:tag], default: basename of archive dir + :latest";
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -9,11 +9,20 @@ module infix-ethernet-interface {
|
||||
import ietf-interfaces {
|
||||
prefix if;
|
||||
}
|
||||
import ietf-yang-types {
|
||||
prefix yang;
|
||||
reference "IETF RFC 6991";
|
||||
}
|
||||
|
||||
organization "KernelKit";
|
||||
contact "kernelkit@googlegroups.com";
|
||||
description "Extensions and deviations to ieee802-ethernet-interface.yang";
|
||||
|
||||
revision 2024-02-27 {
|
||||
description "Add augment for in-good-octets and out-good-octets";
|
||||
reference "internal";
|
||||
}
|
||||
|
||||
revision 2024-01-22 {
|
||||
description "Support ethernet but not negotiation-status";
|
||||
reference "internal";
|
||||
@@ -27,6 +36,18 @@ module infix-ethernet-interface {
|
||||
/*
|
||||
* Data Nodes
|
||||
*/
|
||||
augment "/if:interfaces/if:interface/eth:ethernet/eth:statistics/eth:frame" {
|
||||
leaf out-good-octets {
|
||||
type yang:counter64;
|
||||
units octets;
|
||||
description "A count of data and padding octets of frames that are successfully transmitted.";
|
||||
}
|
||||
leaf in-good-octets {
|
||||
type yang:counter64;
|
||||
units octets;
|
||||
description "A count of data and padding octets in frames that are successfully received.";
|
||||
}
|
||||
}
|
||||
|
||||
/* Deviations for config and status */
|
||||
|
||||
@@ -8,7 +8,7 @@ module infix-hardware {
|
||||
import iana-hardware {
|
||||
prefix iahw;
|
||||
}
|
||||
|
||||
|
||||
import ietf-yang-types {
|
||||
prefix yang;
|
||||
}
|
||||
@@ -25,6 +25,56 @@ module infix-hardware {
|
||||
description "A two-letter country code.";
|
||||
}
|
||||
|
||||
identity hardware-class {
|
||||
description "infix hardware base class";
|
||||
}
|
||||
|
||||
identity usb {
|
||||
base hardware-class;
|
||||
description "This identity is used to describe a USB port";
|
||||
}
|
||||
identity vpd {
|
||||
base hardware-class;
|
||||
description "This identity is used to a VPD memory on the device.";
|
||||
}
|
||||
|
||||
deviation "/iehw:hardware/iehw:component/iehw:class" {
|
||||
deviate replace {
|
||||
type identityref {
|
||||
base hardware-class;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/iehw:hardware/iehw:component/iehw:state/iehw:admin-state" {
|
||||
deviate add {
|
||||
must ". = 'locked' or . = 'unlocked'" {
|
||||
error-message "Only 'locked' and 'unlocked' states are allowed here.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/iehw:hardware/iehw:component/iehw:state/iehw:standby-state" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/iehw:hardware/iehw:component/iehw:sensor-data" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/iehw:hardware/iehw:component/iehw:parent" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/iehw:hardware/iehw:component/iehw:parent-rel-pos" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/iehw:hardware/iehw:component/iehw:alias" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/iehw:hardware/iehw:component/iehw:uri" {
|
||||
deviate not-supported;
|
||||
}
|
||||
deviation "/iehw:hardware/iehw:component/iehw:asset-id" {
|
||||
deviate not-supported;
|
||||
}
|
||||
augment "/iehw:hardware/iehw:component" {
|
||||
container vpd-data {
|
||||
config false;
|
||||
|
||||
+37
@@ -18,6 +18,10 @@ submodule infix-if-bridge {
|
||||
contact "kernelkit@googlegroups.com";
|
||||
description "Linux bridge extension for ietf-interfaces.";
|
||||
|
||||
revision 2024-02-19 {
|
||||
description "Add STP state to bridge port.";
|
||||
reference "internal";
|
||||
}
|
||||
revision 2023-12-02 {
|
||||
description "Extend bridge-port must expression to ensure a
|
||||
bridge cannot be a bridge-port to itself.";
|
||||
@@ -77,6 +81,33 @@ submodule infix-if-bridge {
|
||||
reserved link-local multicast groups, in 01:80:C2:00:00:0X.";
|
||||
}
|
||||
|
||||
typedef stp-state {
|
||||
type enumeration {
|
||||
enum disabled {
|
||||
value 0;
|
||||
description "Port is in STP DISABLED state";
|
||||
}
|
||||
enum listening {
|
||||
value 1;
|
||||
description "Port is in STP LISTENING state";
|
||||
}
|
||||
enum learning {
|
||||
value 2;
|
||||
description "Port is in STP LEARNING state";
|
||||
}
|
||||
enum forwarding {
|
||||
value 3;
|
||||
description "Port is in STP FORWARDING state. This is the default vlan state.";
|
||||
}
|
||||
enum blocking {
|
||||
value 4;
|
||||
description "Port is in STP BLOCKING state.";
|
||||
}
|
||||
}
|
||||
description
|
||||
"User-friendly enumeration of different bridge port operational states.";
|
||||
}
|
||||
|
||||
/*
|
||||
* Data Nodes
|
||||
*/
|
||||
@@ -158,6 +189,12 @@ submodule infix-if-bridge {
|
||||
description "Bridge interface to which this interface is attached.";
|
||||
}
|
||||
|
||||
leaf stp-state {
|
||||
type stp-state;
|
||||
config false;
|
||||
description "The operation state of the bridge port.";
|
||||
}
|
||||
|
||||
leaf pvid {
|
||||
if-feature "vlan-filtering";
|
||||
type dot1q-types:vlanid;
|
||||
@@ -0,0 +1,110 @@
|
||||
submodule infix-if-container {
|
||||
yang-version 1.1;
|
||||
belongs-to infix-interfaces {
|
||||
prefix infix-if;
|
||||
}
|
||||
|
||||
import ietf-inet-types {
|
||||
prefix inet;
|
||||
}
|
||||
import ietf-interfaces {
|
||||
prefix if;
|
||||
}
|
||||
import infix-if-type {
|
||||
prefix infixift;
|
||||
}
|
||||
|
||||
organization "KernelKit";
|
||||
contact "kernelkit@googlegroups.com";
|
||||
description "Container network to interface mapping for ietf-interfaces.
|
||||
Ensures a container interface can never be a bridge port, or
|
||||
LAG member, at the same time.";
|
||||
|
||||
revision 2024-01-15 {
|
||||
description "Initial revision.";
|
||||
reference "internal";
|
||||
}
|
||||
|
||||
/*
|
||||
* Identities
|
||||
*/
|
||||
|
||||
identity container-network {
|
||||
description "Container network type";
|
||||
}
|
||||
|
||||
identity bridge {
|
||||
base container-network;
|
||||
description "Container bridge with IP masquerading, portmappping, and firewalling.";
|
||||
}
|
||||
|
||||
identity host {
|
||||
base container-network;
|
||||
description "Host device, e.g., one end of a VETH pair or other host interface.";
|
||||
}
|
||||
|
||||
/*
|
||||
* Data Nodes
|
||||
*/
|
||||
|
||||
augment "/if:interfaces/if:interface/infix-if:port" {
|
||||
description "Augments the interface model with container networks.";
|
||||
if-feature containers;
|
||||
|
||||
case container-network {
|
||||
container container-network {
|
||||
presence "Container network mapping.";
|
||||
|
||||
leaf type {
|
||||
description "Masquerading container bridge or a host interface";
|
||||
type identityref {
|
||||
base container-network;
|
||||
}
|
||||
}
|
||||
|
||||
list subnet {
|
||||
description "Static IP ranges to hand out addresses to containers from.
|
||||
|
||||
A container bridge forwards DNS, NTP, and SSH by default to
|
||||
the host interfaces.";
|
||||
when "../type = 'infix-if:bridge'";
|
||||
key subnet;
|
||||
|
||||
leaf subnet {
|
||||
type inet:ip-prefix;
|
||||
description "Subnet to assign addresses from, round-robin assignment.
|
||||
|
||||
The default is from a standard Docker setup.";
|
||||
default "172.17.0.0/16";
|
||||
}
|
||||
|
||||
leaf gateway {
|
||||
type inet:ip-address;
|
||||
description "Optional gateway address for the subnet, defaults to .1.
|
||||
|
||||
This will be used as the address of the container bridge.";
|
||||
}
|
||||
}
|
||||
|
||||
list route {
|
||||
description "IPv4 or IPv6 routes to be added to container.
|
||||
|
||||
For bridge type interfaces the gateway can be omitted, the
|
||||
IP address of the container bridge will then be used as the
|
||||
next-hop address.";
|
||||
key subnet;
|
||||
|
||||
leaf subnet {
|
||||
type inet:ip-prefix;
|
||||
description "Destination (subnet) IP address.";
|
||||
}
|
||||
|
||||
leaf gateway {
|
||||
type inet:ip-address;
|
||||
description "Optional gateway (next-hop) IP address for the route.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user