Compare commits
11 Commits
32e78e9bcf
...
f359a64218
| Author | SHA1 | Date | |
|---|---|---|---|
|
f359a64218
|
|||
|
3bc92c5889
|
|||
|
430be75fab
|
|||
|
25e812ab55
|
|||
|
67b644005a
|
|||
|
9eb79d34f1
|
|||
|
9776f8ed9f
|
|||
|
57ec92fc5d
|
|||
|
10786dead3
|
|||
|
a2be3dc1ea
|
|||
|
b78c205c9a
|
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python Debugger: Current File",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -52,21 +52,21 @@ Default user: `ducoterra`
|
||||
Default password: `osbuild`
|
||||
|
||||
- [ ] `passwd ducoterra`
|
||||
- [ ] `hostnamectl hostname <hostname>`
|
||||
- [ ] Updates
|
||||
- [ ] `hostnamectl hostname <hostname>`
|
||||
- [ ] Static IP and DNS address
|
||||
|
||||
```bash
|
||||
# Convert the build to raw
|
||||
qemu-img convert -f qcow2 -O raw \
|
||||
/srv/smb/pool0/ducoterra/images/builds/fedora-43-base.qcow2 \
|
||||
/srv/smb/pool0/ducoterra/images/builds/fedora43-base.qcow2 \
|
||||
/srv/vm/pool1/fedora-boot.raw
|
||||
|
||||
# Install (Change password for default user ducoterra!)
|
||||
virt-install \
|
||||
--boot uefi,firmware.feature0.name=secure-boot,firmware.feature0.enabled=no \
|
||||
--cpu host-passthrough --vcpus sockets=1,cores=8,threads=2 \
|
||||
--ram=8192 \
|
||||
--ram=4096 \
|
||||
--os-variant=fedora41 \
|
||||
--network bridge:bridge0 \
|
||||
--graphics none \
|
||||
@@ -135,7 +135,7 @@ virt-install \
|
||||
--boot uefi,firmware.feature0.name=secure-boot,firmware.feature0.enabled=no \
|
||||
--cpu host-passthrough --vcpus sockets=1,cores=8,threads=2 \
|
||||
--ram=8192 \
|
||||
--os-variant=fedora41 \
|
||||
--os-variant=fedora43 \
|
||||
--network bridge:bridge0 \
|
||||
--graphics none \
|
||||
--console pty,target.type=virtio \
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
- [LG TV Switch](#lg-tv-switch)
|
||||
- [Raspberry Pi Docker](#raspberry-pi-docker)
|
||||
- [Extended OpenAI Conversation](#extended-openai-conversation)
|
||||
- [Templates](#templates)
|
||||
- [List Roku TV Apps](#list-roku-tv-apps)
|
||||
|
||||
## Certificates
|
||||
|
||||
@@ -749,4 +751,49 @@ Setting up a new llama.cpp agent:
|
||||
| Skip Authentication | Yes |
|
||||
| API Provider | OpenAI |
|
||||
|
||||
Model Name: `ggml-org/gpt-oss-120b-GGUF`
|
||||
Model Name: `ggml-org/gpt-oss-120b-GGUF`
|
||||
|
||||
## Templates
|
||||
|
||||
### List Roku TV Apps
|
||||
|
||||
```yaml
|
||||
{{ state_attr("media_player.right_living_room", "source_list") }}
|
||||
|
||||
{% for app in tv_apps -%}
|
||||
"{{ app }}"
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
For extended openai conversation template:
|
||||
|
||||
```yaml
|
||||
- spec:
|
||||
name: set_tv_app
|
||||
description: |-
|
||||
Use this function to open an app on a tv media player.
|
||||
parameters:
|
||||
type: object
|
||||
properties:
|
||||
entity_id:
|
||||
type: string
|
||||
description: entity_id of the TV Media Player
|
||||
source:
|
||||
type: string
|
||||
description: The app you want to open
|
||||
required:
|
||||
- entity_id
|
||||
- source
|
||||
function:
|
||||
type: composite
|
||||
sequence:
|
||||
- type: script
|
||||
sequence:
|
||||
- service:
|
||||
|
||||
- service: media_player.select_source
|
||||
target:
|
||||
entity_id: "{{ entity_id }}"
|
||||
data:
|
||||
source: "{{ source }}"
|
||||
```
|
||||
|
||||
@@ -11,3 +11,10 @@ helm: <https://helm.sh/docs/intro/install/>
|
||||
For k3s, see [k3s](/active/software_k3s/k3s.md)
|
||||
|
||||
For k0s, see [k0s](/active/software_k0s/k0s.md)
|
||||
|
||||
## Notes
|
||||
|
||||
```bash
|
||||
# Quickly set a new namespace
|
||||
kubectl config set contexts.default.namespace metallb-system
|
||||
```
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
apiVersion: metallb.io/v1beta1
|
||||
kind: IPAddressPool
|
||||
metadata:
|
||||
name: unifi-pool
|
||||
namespace: kube-system
|
||||
name: default-pool
|
||||
namespace: metallb-system
|
||||
spec:
|
||||
addresses:
|
||||
- 2603:6013:3140:105:10:5:0:10-2603:6013:3140:105:10:5:0:210
|
||||
- 10.5.0.10-10.5.0.210
|
||||
- 10.4.1.1-10.4.3.254
|
||||
|
||||
---
|
||||
apiVersion: metallb.io/v1beta1
|
||||
kind: L2Advertisement
|
||||
metadata:
|
||||
name: l2advertisement
|
||||
namespace: kube-system
|
||||
namespace: metallb-system
|
||||
spec:
|
||||
ipAddressPools:
|
||||
- unifi-pool
|
||||
- default-pool
|
||||
|
||||
@@ -15,33 +15,31 @@ spec:
|
||||
app.kubernetes.io/name: ingress-nginx-demo-1
|
||||
spec:
|
||||
containers:
|
||||
- name: httpd
|
||||
image: httpd
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
resources:
|
||||
requests:
|
||||
memory: "100Mi"
|
||||
cpu: "1m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "1"
|
||||
- name: httpd
|
||||
image: httpd
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
resources:
|
||||
requests:
|
||||
memory: "100Mi"
|
||||
cpu: "1m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "1"
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ingress-nginx-demo-1
|
||||
namespace: default
|
||||
annotations:
|
||||
metallb.universe.tf/address-pool: "unifi-pool"
|
||||
metallb.universe.tf/address-pool: "default-pool"
|
||||
spec:
|
||||
ipFamilyPolicy: PreferDualStack
|
||||
ipFamilies:
|
||||
- IPv6
|
||||
- IPv4
|
||||
- IPv4
|
||||
type: LoadBalancer
|
||||
ports:
|
||||
- name: http
|
||||
43
active/kubernetes_metallb/metallb.md
Normal file
43
active/kubernetes_metallb/metallb.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Metal LB
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
# Set a manual IP if you have a no-dhcp network
|
||||
nmcli connection modify "Wired connection 2" \
|
||||
ipv4.method manual \
|
||||
ipv4.gateway 10.4.0.1 \
|
||||
ipv4.addresses 10.4.0.3/22
|
||||
|
||||
# Clear it if you have a dhcp network
|
||||
nmcli connection modify "Wired connection 2" \
|
||||
ipv4.addresses "" \
|
||||
ipv4.gateway "" \
|
||||
ipv4.method auto
|
||||
|
||||
# Bring up the interface
|
||||
nmcli connection up "Wired connection 2"
|
||||
|
||||
# Create the metallb namespace
|
||||
kubectl apply -f active/kubernetes_metallb/namespace.yaml
|
||||
|
||||
# Install metallb
|
||||
helm repo add metallb https://metallb.github.io/metallb
|
||||
helm repo update
|
||||
helm upgrade --install metallb \
|
||||
--namespace metallb-system \
|
||||
metallb/metallb
|
||||
|
||||
# Check that the pods installed correctly
|
||||
kubectl get pod -n metallb-system
|
||||
|
||||
# Install the address pool
|
||||
# NOTE: Edit this to match your own allocated addresses!
|
||||
kubectl apply -f active/kubernetes_metallb/addresspool.yaml
|
||||
|
||||
# Test that the service is working. This will spin up a web server on port 8001
|
||||
kubectl apply -f active/kubernetes_metallb/metallb-test.yaml
|
||||
|
||||
# Delete the test
|
||||
kubectl delete -f active/kubernetes_metallb/metallb-test.yaml
|
||||
```
|
||||
8
active/kubernetes_metallb/namespace.yaml
Normal file
8
active/kubernetes_metallb/namespace.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: metallb-system
|
||||
labels:
|
||||
pod-security.kubernetes.io/enforce: privileged
|
||||
pod-security.kubernetes.io/audit: privileged
|
||||
pod-security.kubernetes.io/warn: privileged
|
||||
@@ -45,7 +45,10 @@
|
||||
- [Benchmark Results](#benchmark-results)
|
||||
- [Testing with Curl](#testing-with-curl)
|
||||
- [OpenAI API](#openai-api)
|
||||
- [VLLM](#vllm)
|
||||
- [Run VLLM with Podman](#run-vllm-with-podman)
|
||||
- [Misc](#misc)
|
||||
- [Quantizing your own Models](#quantizing-your-own-models)
|
||||
- [Qwen3.5 Settings](#qwen35-settings)
|
||||
|
||||
## Notes
|
||||
@@ -215,6 +218,11 @@ hf download --local-dir . ggml-org/Ministral-3-3B-Instruct-2512-GGUF
|
||||
##### Qwen
|
||||
|
||||
```bash
|
||||
# qwen3.5-27b-opus
|
||||
mkdir qwen3.5-27b-opus && qwen3.5-27b-opus
|
||||
hf download --local-dir . Jackrong/Qwen3.5-27B-Claude-4.6-Opus-Reasoning-Distilled-GGUF Qwen3.5-27B.Q4_K_M.gguf
|
||||
hf download --local-dir . Jackrong/Qwen3.5-27B-Claude-4.6-Opus-Reasoning-Distilled-GGUF mmproj-BF16.gguf
|
||||
|
||||
# qwen3.5-4b
|
||||
mkdir qwen3.5-4b && cd qwen3.5-4b
|
||||
hf download --local-dir . unsloth/Qwen3.5-4B-GGUF Qwen3.5-4B-Q8_0.gguf
|
||||
@@ -264,6 +272,17 @@ hf download --local-dir . unsloth/GLM-4.7-Flash-GGUF GLM-4.7-Flash-Q8_0.gguf
|
||||
|
||||
```bash
|
||||
# Note "it" vs "pt" suffixes. "it" is instruction following, "pt" is the base model (not as good for out-of-the-box use)
|
||||
|
||||
# gemma-4-26b-a4b
|
||||
mkdir gemma-4-26b-a4b && cd gemma-4-26b-a4b
|
||||
hf download --local-dir . ggml-org/gemma-4-26B-A4B-it-GGUF gemma-4-26B-A4B-it-Q8_0.gguf
|
||||
hf download --local-dir . ggml-org/gemma-4-26B-A4B-it-GGUF mmproj-gemma-4-26B-A4B-it-f16.gguf
|
||||
|
||||
# gemma-4-31b
|
||||
mkdir gemma-4-31b && cd gemma-4-31b
|
||||
hf download --local-dir . ggml-org/gemma-4-31B-it-GGUF gemma-4-31B-it-Q8_0.gguf
|
||||
hf download --local-dir . ggml-org/gemma-4-31B-it-GGUF mmproj-gemma-4-31B-it-f16.gguf
|
||||
|
||||
# gemma-3-27b-it
|
||||
mkdir gemma-3-27b-it && cd gemma-3-27b-it
|
||||
hf download --local-dir . unsloth/gemma-3-27b-it-GGUF gemma-3-27b-it-Q8_0.gguf
|
||||
@@ -353,7 +372,7 @@ podman build -f .devops/vulkan.Dockerfile -t llama-cpp-vulkan:${BUILD_TAG} -t ll
|
||||
# ROCM
|
||||
podman build -f .devops/rocm.Dockerfile -t llama-cpp-rocm:${BUILD_TAG} -t llama-cpp-rocm:latest .
|
||||
|
||||
# Run llama demo server (Available on port 8000)
|
||||
# Run llama demo server (Available on port 8010)
|
||||
podman run \
|
||||
--rm \
|
||||
--name llama-server-demo \
|
||||
@@ -361,10 +380,11 @@ podman run \
|
||||
--device=/dev/dri \
|
||||
-v /home/ai/models/text:/models:z \
|
||||
-p 8010:8000 \
|
||||
--ipc host \
|
||||
localhost/llama-cpp-vulkan:latest \
|
||||
--host 0.0.0.0 \
|
||||
--port 8000 \
|
||||
-c 16000 \
|
||||
-c 128000 \
|
||||
--perf \
|
||||
--n-gpu-layers all \
|
||||
--jinja \
|
||||
@@ -756,8 +776,64 @@ curl \
|
||||
}'
|
||||
```
|
||||
|
||||
## VLLM
|
||||
|
||||
### Run VLLM with Podman
|
||||
|
||||
```bash
|
||||
# 'latest' and 'nightly' are both viable tags
|
||||
podman run --rm \
|
||||
--device /dev/kfd \
|
||||
--device /dev/dri \
|
||||
-v ~/.cache/huggingface:/root/.cache/huggingface:z \
|
||||
--env "HF_TOKEN=$HF_TOKEN" \
|
||||
-p 8010:8000 \
|
||||
--ipc=host \
|
||||
docker.io/vllm/vllm-openai-rocm:nightly \
|
||||
--enable-offline-docs \
|
||||
|
||||
# Pick your model
|
||||
Qwen/Qwen3.5-35B-A3B --max-model-len 262144 --reasoning-parser qwen3 --enable-auto-tool-choice --tool-call-parser qwen3_coder
|
||||
Qwen/Qwen3.5-9B --max-model-len 262144 --reasoning-parser qwen3 --enable-auto-tool-choice --tool-call-parser qwen3_coder
|
||||
Qwen/Qwen3.5-35B-A3B-FP8
|
||||
google/gemma-4-26B-A4B-it
|
||||
openai/gpt-oss-120b
|
||||
|
||||
```
|
||||
|
||||
## Misc
|
||||
|
||||
### Quantizing your own Models
|
||||
|
||||
```bash
|
||||
# Create a scratch dir for downloading models
|
||||
mkdir scratch && cd scratch
|
||||
|
||||
# qwen 3.5 35b
|
||||
mkdir qwen3.5-35b-a3b && cd qwen3.5-35b-a3b
|
||||
hf download --local-dir . Qwen/Qwen3.5-35B-A3B
|
||||
|
||||
# nemotron cascade
|
||||
mkdir nemotron-cascade-2-30b-a3b && cd nemotron-cascade-2-30b-a3b
|
||||
hf download --local-dir . nvidia/Nemotron-Cascade-2-30B-A3B
|
||||
|
||||
# Run the full
|
||||
podman run -it --rm \
|
||||
--device=/dev/kfd \
|
||||
--device=/dev/dri \
|
||||
-v $(pwd):/models:z \
|
||||
--entrypoint /bin/bash \
|
||||
ghcr.io/ggml-org/llama.cpp:full-vulkan
|
||||
|
||||
# Run ./llama-quantize to see available quants
|
||||
# 7 = q_8
|
||||
# 18 = q_6_k
|
||||
# 17 = q_5_k
|
||||
# 15 = q_4_k
|
||||
./llama-quantize /models/$MODEL_NAME.gguf /models/$MODEL_NAME-Q6_K.gguf 18
|
||||
./llama-quantize /models/$MODEL_NAME.gguf /models/$MODEL_NAME-Q8_0.gguf 7
|
||||
```
|
||||
|
||||
### Qwen3.5 Settings
|
||||
|
||||
> We recommend using the following set of sampling parameters for generation
|
||||
|
||||
@@ -23,6 +23,10 @@ firewall-cmd --info-service=samba
|
||||
|
||||
# Get zone information
|
||||
firewall-cmd --info-zone=drop
|
||||
|
||||
# Logging
|
||||
firewall-cmd --set-log-denied=all
|
||||
dmesg --follow | egrep -i 'REJECT|DROP'
|
||||
```
|
||||
|
||||
## Inspecting Zones
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
- [K3S](#k3s)
|
||||
- [Guide](#guide)
|
||||
- [Firewalld](#firewalld)
|
||||
- [Set SELinux to Permissive](#set-selinux-to-permissive)
|
||||
- [Install K3S (Single Node)](#install-k3s-single-node)
|
||||
- [SELinux](#selinux)
|
||||
- [Install Single Node K3S](#install-single-node-k3s)
|
||||
- [Dual Stack IPv6 Support](#dual-stack-ipv6-support)
|
||||
- [Single Stack IPv4](#single-stack-ipv4)
|
||||
- [Install Multi Node K3S](#install-multi-node-k3s)
|
||||
- [Network Checks](#network-checks)
|
||||
- [Kube Credentials](#kube-credentials)
|
||||
- [Metal LB](#metal-lb)
|
||||
- [VLAN Setup](#vlan-setup)
|
||||
@@ -35,25 +37,49 @@
|
||||
## Firewalld
|
||||
|
||||
```bash
|
||||
firewall-cmd --permanent --zone=public --add-port=6443/tcp # apiserver
|
||||
firewall-cmd --permanent --zone=trusted --add-source=10.42.0.0/16 # pods
|
||||
firewall-cmd --permanent --zone=trusted --add-source=fd02:c91e:56f4::/56 # pods
|
||||
firewall-cmd --permanent --zone=trusted --add-source=10.43.0.0/16 # services
|
||||
firewall-cmd --permanent --zone=trusted --add-source=fd02:c91e:56f5::/112 # services
|
||||
# All required ports (https://docs.k3s.io/installation/requirements?_highlight=ports#local-ports)
|
||||
firewall-cmd \
|
||||
--permanent \
|
||||
--zone=public \
|
||||
--add-port=80/tcp \
|
||||
--add-port=443/tcp \
|
||||
--add-port=2379-2380/tcp \
|
||||
--add-port=6443/tcp \
|
||||
--add-port=8472/udp \
|
||||
--add-port=10250/tcp
|
||||
|
||||
# IPv4 config
|
||||
# 10.42 is for pods
|
||||
# 10.43 is for services
|
||||
firewall-cmd \
|
||||
--permanent \
|
||||
--zone=trusted \
|
||||
--add-source=10.42.0.0/16 \
|
||||
--add-source=10.43.0.0/16
|
||||
|
||||
# [Optional] IPv6 config
|
||||
# fd02:c91e:56f4 is for pods
|
||||
# fd02:c91e:56f5 is for services
|
||||
firewall-cmd \
|
||||
--permanent \
|
||||
--zone=trusted \
|
||||
--add-source=fd02:c91e:56f4::/56 \
|
||||
--add-source=fd02:c91e:56f5::/112
|
||||
|
||||
firewall-cmd --reload
|
||||
```
|
||||
|
||||
## Set SELinux to Permissive
|
||||
## SELinux
|
||||
|
||||
Make sure to add `--selinux` to your install script.
|
||||
|
||||
## Install K3S (Single Node)
|
||||
## Install Single Node K3S
|
||||
|
||||
### Dual Stack IPv6 Support
|
||||
|
||||
```bash
|
||||
curl -sfL https://get.k3s.io | sh -s - \
|
||||
--selinux \
|
||||
"--disable" \
|
||||
"traefik" \
|
||||
"--disable" \
|
||||
@@ -67,8 +93,7 @@ curl -sfL https://get.k3s.io | sh -s - \
|
||||
"--service-cidr" \
|
||||
"10.43.0.0/16,fd02:c91e:56f5::/112" \
|
||||
"--cluster-dns" \
|
||||
"fd02:c91e:56f5::10" \
|
||||
--selinux
|
||||
"fd02:c91e:56f5::10"
|
||||
```
|
||||
|
||||
### Single Stack IPv4
|
||||
@@ -84,16 +109,67 @@ curl -sfL https://get.k3s.io | sh -s - \
|
||||
--selinux
|
||||
```
|
||||
|
||||
## Install Multi Node K3S
|
||||
|
||||
TODO: haproxy (<https://docs.k3s.io/blog/2025/03/10/simple-ha?_highlight=tls&_highlight=san#load-balancer>)
|
||||
Load balance a single registration point across all active nodes.
|
||||
|
||||
```bash
|
||||
# Generate a shared token for joining nodes
|
||||
# Copy this token to each node at ~/.k3s-token
|
||||
pwgen --capitalize --numerals --secure 64 1 > ~/.k3s-token
|
||||
|
||||
# Create the first node
|
||||
curl -sfL https://get.k3s.io | K3S_TOKEN=$(cat ~/.k3s-token) sh -s - \
|
||||
--cluster-init \
|
||||
--selinux \
|
||||
"--disable" \
|
||||
"traefik" \
|
||||
"--disable" \
|
||||
"servicelb" \
|
||||
"--cluster-cidr" \
|
||||
"10.42.0.0/16" \
|
||||
"--service-cidr" \
|
||||
"10.43.0.0/16"
|
||||
|
||||
# Copy the generated token to the other nodes
|
||||
cat /var/lib/rancher/k3s/server/token
|
||||
|
||||
# Join nodes
|
||||
curl -sfL https://get.k3s.io | K3S_TOKEN=$(cat ~/.k3s-token) sh -s - \
|
||||
--selinux \
|
||||
"--disable" \
|
||||
"traefik" \
|
||||
"--disable" \
|
||||
"servicelb" \
|
||||
"--cluster-cidr" \
|
||||
"10.42.0.0/16" \
|
||||
"--service-cidr" \
|
||||
"10.43.0.0/16" \
|
||||
--server https://kube1.reeselink.com:6443
|
||||
```
|
||||
|
||||
## Network Checks
|
||||
|
||||
At this point it's a good idea to make sure node communication is working as expected.
|
||||
|
||||
```bash
|
||||
firewall-cmd --set-log-denied=all
|
||||
# You shouldn't see any dropped traffic from your nodes.
|
||||
dmesg --follow | egrep -i 'REJECT|DROP'
|
||||
```
|
||||
|
||||
## Kube Credentials
|
||||
|
||||
On the operator
|
||||
|
||||
```bash
|
||||
export KUBE_SERVER_ADDRESS="https://k3s.reeselink.com:6443"
|
||||
export KUBE_SERVER_ADDRESS="https://kube1.reeselink.com:6443"
|
||||
# Copy the kube config down
|
||||
ssh k3s cat /etc/rancher/k3s/k3s.yaml | \
|
||||
yq -y ".clusters[0].cluster.server = \"${KUBE_SERVER_ADDRESS}\"" > \
|
||||
ssh kube1-root cat /etc/rancher/k3s/k3s.yaml | \
|
||||
yq -r ".clusters[0].cluster.server = \"${KUBE_SERVER_ADDRESS}\"" > \
|
||||
~/.kube/admin-kube-config
|
||||
export KUBECONFIG=~/.kube/admin-kube-config
|
||||
```
|
||||
|
||||
## Metal LB
|
||||
@@ -141,15 +217,7 @@ IP. When that node goes down metallb simply advertises a new mac address for the
|
||||
address, effectively moving the IP to another node. This isn't really "load balancing" but
|
||||
"failover". Fortunately, that's exactly what we're looking for.
|
||||
|
||||
```bash
|
||||
helm repo add metallb https://metallb.github.io/metallb
|
||||
helm repo update
|
||||
|
||||
# Install metallb
|
||||
helm upgrade --install metallb \
|
||||
--namespace kube-system \
|
||||
metallb/metallb
|
||||
```
|
||||
[Install MetalLB](/active/kubernetes_metallb/metallb.md)
|
||||
|
||||
MetalLB doesn't know what IP addresses are available for it to allocate so
|
||||
we'll have to provide it with a list. The
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name = "fedora43-base"
|
||||
description = "Fedora Base Installation"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
distro = "fedora-43"
|
||||
modules = []
|
||||
groups = []
|
||||
|
||||
@@ -47,7 +47,7 @@ dnf install openscap-scanner scap-security-guide
|
||||
2. Push the toml to composer
|
||||
|
||||
```bash
|
||||
composer-cli blueprints push active/software_osbuild/fedora42-base.toml
|
||||
composer-cli blueprints push active/software_osbuild/fedora43-base.toml
|
||||
|
||||
# List blueprints
|
||||
composer-cli blueprints list
|
||||
@@ -60,7 +60,7 @@ dnf install openscap-scanner scap-security-guide
|
||||
composer-cli compose types
|
||||
|
||||
# Build the image
|
||||
composer-cli compose start fedora42-base qcow2
|
||||
composer-cli compose start fedora43-base qcow2
|
||||
|
||||
# Check status
|
||||
watch composer-cli compose status
|
||||
@@ -94,7 +94,7 @@ dnf install openscap-scanner scap-security-guide
|
||||
--network bridge:virbr0 \
|
||||
--graphics none \
|
||||
--console pty,target.type=virtio \
|
||||
--import --disk "path=active/software_osbuild/secrets/fedora43-base.qcow2,bus=virtio"
|
||||
--import --disk "path=active/software_osbuild/secrets/fedora43base.qcow2,bus=virtio"
|
||||
```
|
||||
|
||||
### Image Build and Watch One Liner
|
||||
|
||||
@@ -72,7 +72,8 @@ script/run \
|
||||
--uri 'tcp://127.0.0.1:10400' \
|
||||
--threshold '0.8' \
|
||||
--preload-model 'jarvis_v2' \
|
||||
--debug-probability
|
||||
--debug \
|
||||
--custom-model-dir /home/ducoterra/models
|
||||
```
|
||||
|
||||
## Install Wyoming
|
||||
@@ -87,21 +88,21 @@ uv pip install .
|
||||
uv pip install webrtc-noise-gain==1.2.3
|
||||
|
||||
# Copy listen and done sounds
|
||||
cp ~/Homelab/active/systemd_wyoming/{listening.wav,finished.wav} ~/wyoming-satellite/sounds
|
||||
scp /active/software_wyoming/{listening.wav,finished.wav} ~/wyoming-satellite/sounds
|
||||
|
||||
# typical wyoming command
|
||||
# Add wake-uri and wake-word-name to your wyoming run
|
||||
script/run \
|
||||
--name 'Living Room' \
|
||||
--uri 'tcp://0.0.0.0:10700' \
|
||||
--mic-command 'arecord -r 16000 -c 1 -f S16_LE -t raw' \
|
||||
--snd-command 'aplay -r 22050 -c 1 -f S16_LE -t raw' \
|
||||
--awake-wav /root/wyoming-satellite/sounds/listening.wav \
|
||||
--done-wav /root/wyoming-satellite/sounds/finished.wav \
|
||||
--mic-command 'arecord -r 16000 -c 1 -f S16_LE -t raw -D plughw:CARD=Speaker,DEV=0' \
|
||||
--snd-command 'aplay -r 22050 -c 1 -f S16_LE -t raw -D plughw:CARD=Speaker,DEV=0' \
|
||||
--awake-wav /home/ducoterra/wyoming-satellite/sounds/listening.wav \
|
||||
--done-wav /home/ducoterra/wyoming-satellite/sounds/finished.wav \
|
||||
--synthesize-command tee \
|
||||
--transcript-command tee \
|
||||
--wake-uri 'tcp://127.0.0.1:10400' \
|
||||
--wake-word-name 'hey dick head' \
|
||||
--wake-word-name 'hey jarvis' \
|
||||
--wake-refractory-seconds 1
|
||||
|
||||
# Allow through firewall
|
||||
|
||||
141
active/vibe_system_report/agent_check_health.py
Normal file
141
active/vibe_system_report/agent_check_health.py
Normal file
@@ -0,0 +1,141 @@
|
||||
import os
|
||||
import subprocess
|
||||
from functools import wraps
|
||||
from typing import Callable
|
||||
|
||||
from langchain.agents import create_agent
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
|
||||
def make_verbose(func: Callable):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
print("==========")
|
||||
print(f"Calling {func.__name__} with params ({', '.join(args)}) and ({kwargs})")
|
||||
result = func(*args, **kwargs)
|
||||
print("==========")
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def run_command(command: list[str]) -> str:
|
||||
"""Runs a command with subprocess.run and returns the stdout, stderr in a single string"""
|
||||
process = subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True,
|
||||
)
|
||||
stdouts: list[str] = []
|
||||
stderrs: list[str] = []
|
||||
if process.stdout:
|
||||
print("**STDOUT**")
|
||||
for line in process.stdout:
|
||||
line = line.strip()
|
||||
print(line)
|
||||
stdouts.append(line)
|
||||
|
||||
if process.stderr:
|
||||
print("**STDERR**")
|
||||
for line in process.stderr:
|
||||
line = line.strip()
|
||||
print(line)
|
||||
stderrs.append(line)
|
||||
|
||||
output = f"**STDOUT**\n{'\n'.join(stdouts)}\n\n**STDERR**{'\n'.join(stderrs)}"
|
||||
return output
|
||||
|
||||
|
||||
@make_verbose
|
||||
def get_fstab() -> str:
|
||||
"""Retruns the"""
|
||||
output = run_command(["ssh", "driveripper", "virsh", "list", "--name"])
|
||||
return output
|
||||
|
||||
|
||||
@make_verbose
|
||||
def check_vm_type(server_name: str) -> str:
|
||||
"""Returns various information about a given server like OS and version. server_name should be a server from list_vms."""
|
||||
output = run_command(["ssh", f"{server_name}-root", "cat", "/etc/*-release"])
|
||||
return output
|
||||
|
||||
|
||||
@make_verbose
|
||||
def get_updates_fedora(server_name: str) -> str:
|
||||
"""Check for updates for a given Fedora server"""
|
||||
output = run_command(["ssh", f"{server_name}-root"])
|
||||
return output
|
||||
|
||||
|
||||
@make_verbose
|
||||
def get_security_updates_fedora(server_name: str) -> str:
|
||||
"""Checks only for security updates for a given Fedora server"""
|
||||
output = run_command(
|
||||
["ssh", f"{server_name}-root", "dnf", "check-update", "--security"]
|
||||
)
|
||||
return output
|
||||
|
||||
|
||||
@make_verbose
|
||||
def perform_security_updates_fedora(server_name: str) -> str:
|
||||
"""Applies security updates for a given Fedora server"""
|
||||
output = run_command(
|
||||
["ssh", f"{server_name}-root", "dnf", "update", "--security", "-y"]
|
||||
)
|
||||
return output
|
||||
|
||||
|
||||
@make_verbose
|
||||
def perform_security_updates_ubuntu(server_name: str) -> str:
|
||||
"""Applies security updates for a given Ubuntu server"""
|
||||
output = run_command(["ssh", f"{server_name}-root", "apt", "update", "-y"])
|
||||
return output
|
||||
|
||||
|
||||
def get_api_key() -> str:
|
||||
return os.getenv("OPENAI_API_KEY", "placeholder")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Run the agent
|
||||
llm = ChatOpenAI(
|
||||
model="instruct",
|
||||
base_url="https://llama-instruct.reeselink.com",
|
||||
api_key=get_api_key,
|
||||
temperature=0.7,
|
||||
timeout=30,
|
||||
max_retries=2,
|
||||
verbose=True,
|
||||
top_p=1,
|
||||
)
|
||||
|
||||
agent = create_agent(
|
||||
model=llm,
|
||||
tools=[
|
||||
list_vms,
|
||||
check_vm_type,
|
||||
get_updates_fedora,
|
||||
get_security_updates_fedora,
|
||||
perform_security_updates_fedora,
|
||||
perform_security_updates_ubuntu,
|
||||
],
|
||||
system_prompt="You are a helpful assistant",
|
||||
)
|
||||
result = agent.invoke(
|
||||
{
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": (
|
||||
"List all the available servers. Then, for each server, check the "
|
||||
"server's OS and use the appropriate update check tool to check for "
|
||||
"security updates. If any server needs security updates, apply them with "
|
||||
"the appropriate update tool. Finally, provide a brief summary of what "
|
||||
"you did."
|
||||
),
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
print(result["messages"][-1].content)
|
||||
@@ -100,13 +100,14 @@ def get_api_key() -> str:
|
||||
if __name__ == "__main__":
|
||||
# Run the agent
|
||||
llm = ChatOpenAI(
|
||||
model="qwen3.5-35b-a3b",
|
||||
base_url="https://llama-cpp.reeselink.com",
|
||||
model="instruct",
|
||||
base_url="https://llama-instruct.reeselink.com",
|
||||
api_key=get_api_key,
|
||||
temperature=0.95,
|
||||
temperature=0.7,
|
||||
timeout=30,
|
||||
max_retries=2,
|
||||
verbose=True,
|
||||
top_p=1,
|
||||
)
|
||||
|
||||
agent = create_agent(
|
||||
|
||||
Reference in New Issue
Block a user