├── .gitignore
├── .gitmodules
├── README.md
├── create-user-daniel.sh
├── demo.sh
├── elasticsearch
├── demo.sh
├── generate-yaml.sh
└── kibana.data-view.sh
├── generate-data
├── .gitignore
├── Containerfile
├── demo.js
├── generate-data.js
├── mappings.curl
├── package.json
├── public
│ └── index.html
└── rebuild.sh
├── hybrid-virt-elasticsearch.webp
├── net.yaml
├── rbac.yaml
├── setup.template.yaml
├── vm.template.yaml
└── windows
├── .gitignore
├── autounattended.xml
├── vm.yaml.template
└── windows2019.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | subscription.txt
2 | *id_rsa*
3 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "format"]
2 | path = format
3 | url = https://github.com/purefield/format.git
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Openshift Virtualization Demo running Elasticsearch on Mixed Pods (VMs and Containers)
2 |
3 | 
4 |
5 | ### Dependencies:
6 | * OpenShift Baremetal cluster with ODF
7 | * ODF is default storage class set and profile applied
8 | * Installed Operators
9 | * OpenShift Virtualization Operator
10 | * Cert Utils Operator
11 | * RHEL Subscription [Activation Keys](https://console.redhat.com/insights/connector/activation-keys) (for Pod software installation)
12 | * Subscription Org
13 | * Subscription Key
14 |
15 | ### Setup
16 | NOTE: Setup is ran from the desired bastion host for the OCP cluster(s)
17 | ```sh
18 | sudo dnf install -y git
19 | git clone https://github.com/purefield/opc-virt.git ~/demo
20 | cd ~/demo/
21 | ./setup.sh
22 | ```
23 |
24 | ### Demo Prep
25 | Log into the OpenShift Cluster
26 |
27 | Run the demo script from the same bastion to create a fresh elasticsearch cluster in a new namespace (first argument or prompt, defaults to ```ocp-virt```)
28 | ```sh
29 | ./demo.sh next-gen-virt
30 | ```
31 | Wait for installation (approximately 4 mins in normal circumstances)
32 |
33 | ### Demo Highlights
34 | Under Administrator Perspective:
35 | * Operators -> OperatorHub
36 | * OpenShift Virtualization Operator (installed)
37 | * Migration Toolkit for Virtualization Operator (available)
38 | * Virtualization
39 | * Overview
40 | * Templates
41 | * DataStore (RWX)
42 | * Catalog
43 | * VirtualMachines
44 | * Create
45 | * With Wizard
46 | * Create
47 | * Select Red Hat Enterprise Linux 9.0 VM
48 | * Next
49 | * Create virtual machine
50 | * [... menu] Migrate Node to Node
51 | * [New VM] Overview
52 | * Virtual Machine options
53 | * Metrics
54 | * Snapshots
55 | * Run demo steps
56 | * Pick random/suggested name for namespace to pass as first argument or when prompted
57 | * Execute generate and apply steps in shared terminal window showing VirtualMachines screen in the background
58 |
59 | Under the Developer Perspective:
60 | * Topology
61 | * Show the mixed environment and dig into details
62 | * 3 Elasticsearch VMs for data and control plane
63 | * 1 Elasticsearch container as coordinator node
64 | * 1 Kibana container for data visualization
65 | * 1 RHEL 9 container for data generation
66 | * 1 RHEL 9 container for utilities to explore with ssh keys to the VMs
67 |
68 | Under Administrator Perspective:
69 | * Networking
70 | * Routes
71 | * Select es-master00 Cockpit route
72 | * Log-in using ```elasticsearch```:```redhat```
73 | * View services and search for elasticsearch (will appear when ready)
74 | * Select elasticsearch route
75 | * Verify ```cluster_uuid``` is populated
76 | * Append ```/_cat/nodes``` to elasticsearch url and verify ```coordinate``` is a member
77 | * Open kibana route to show connected web application
78 | * In Kibana create a new discovery for the "generated" index with timestamp
79 | * Virtualization
80 | * VirtualMachines
81 | * es-master00
82 | * Environment
83 | * Configuration Maps mounted inside the VM
84 | * 00yaml - Elasticsearch Node specific configuration
85 | * 0000sh - Multiple scripts used during VM installation
86 | * Secret mounted inside the VM
87 | * 00cert - Let's Encrypt SSL Certificate private key and chain used by the VMs
88 | * Migrate VM from Node to Node
89 | * Take a Snapshot of the VM
90 |
--------------------------------------------------------------------------------
/create-user-daniel.sh:
--------------------------------------------------------------------------------
1 | . ./format/format.sh
2 | __ "Create htpasswd secret" 4
3 | cmd "oc create secret generic htpasswd -n openshift-config --from-literal htpasswd='daniel:$2y$05$GQlPEZ03BPQ4ZzkPD4dhKeM54VjDPN9YtyORDRVT6w.kU1p4Y4ZZ.'"
4 |
5 | __ "Create htpasswd provider" 4
6 | patch='[{"op":"add","path":"/spec/identityProviders/-","value":{"htpasswd":{"fileData":{"name":"htpasswd"}},"mappingMethod":"claim","name":"htpasswd","type":"HTPasswd"}}]'
7 | cmd "oc patch oauth cluster --type='json' -p='$patch'"
8 |
--------------------------------------------------------------------------------
/demo.sh:
--------------------------------------------------------------------------------
1 | . ./format/format.sh
2 | NAMESPACE=$1
3 | VMS=$2
4 | CUSTOMER=${3:-Our Valued Customer}
5 | __ "OpenShift Virtualization Demo" 1
6 | __ "Configuration" 2
7 |
8 | __ "Namespace" 3
9 | _? "What namespace should we use?" NAMESPACE ocp-virt $NAMESPACE
10 |
11 | __ "Number VMs" 3
12 | _? "How many Elasticsearch VMs should we use?" vms 3 $VMS
13 |
14 | __ "Base Domain" 3
15 | BASEDOMAIN=$(oc get --namespace openshift-ingress-operator ingresscontrollers/default -o jsonpath='{.status.domain}')
16 | _? "What base domain should we use?" BASEDOMAIN "" $BASEDOMAIN
17 |
18 | __ "Red Hat Subscription" 3
19 | if [ -s subscription.txt ]; then . subscription.txt; fi
20 | _? "Key" SUBSCRIPTION_KEY key $SUBSCRIPTION_KEY
21 | _? "ORG" SUBSCRIPTION_ORG org $SUBSCRIPTION_ORG
22 |
23 | __ "Create resources" 2
24 | if [ ! -s ./ssh.id_rsa ]; then
25 | __ "Create ssh keys" 3
26 | cmd 'ssh-keygen -m PEM -N "" -f ./ssh.id_rsa'
27 | fi
28 | export SSH_PUBLIC_KEY="$(cat ssh.id_rsa.pub)"
29 | export SSH_PRIVATE_KEY="$(cat ssh.id_rsa)"
30 |
31 | if [[ "$(oc get project $NAMESPACE -o=jsonpath='{.metadata.name}' 2>/dev/null)" != $NAMESPACE ]]; then
32 | __ "Create namespace" 3
33 | cmd "oc new-project $NAMESPACE --display-name='$CUSTOMER'"
34 | fi
35 |
36 | __ "Create common resources" 3
37 | if [[ "$(oc get secrets/wildcard-cert -n $NAMESPACE -o name 2>/dev/null)" != 'secret/wildcard-cert' ]]; then
38 | __ "For demo purpose re-use the ingress cert" 4
39 | item="secret/letsencrypt-prod-private-key -n openshift-ingress"
40 | cmd "oc get $item -o yaml | sed 's/namespace: openshift-ingress/namespace: $NAMESPACE/' | sed 's/name: .*/name: wildcard-cert/' | oc apply -f -"
41 | fi
42 |
43 | __ "Import setup template into cluster" 4
44 | cmd oc apply -f setup.template.yaml -n openshift
45 | __ "Process parameters and apply" 4
46 | cmd 'oc process ocp-virt-demo-setup-template -n openshift -p NAMESPACE='$NAMESPACE' -p BASEDOMAIN="'$BASEDOMAIN'" -p SUBSCRIPTION_ORG=$SUBSCRIPTION_ORG -p SUBSCRIPTION_KEY=$SUBSCRIPTION_KEY -p SSH_PRIVATE_KEY="$SSH_PRIVATE_KEY" -p SSH_PUBLIC_KEY="$SSH_PUBLIC_KEY" -p SHARDS=$vms | oc apply -f -'
47 | ___ "We use our imported setup template to instantiate our environment" 10
48 |
49 | __ "Create virtual machines" 3
50 | __ "Import vm template into cluster" 4
51 | cmd oc apply -f vm.template.yaml -n openshift
52 | for i in $(seq 0 $((vms -1))); do
53 | name=$(printf "es-master%02d" "$i")
54 | __ "Process parameters for $name and apply" 4
55 | cmd 'oc process ocp-virt-demo-vms-template -n openshift -p VMNAME='$name' -p NAMESPACE='$NAMESPACE' -p BASEDOMAIN="'$BASEDOMAIN'" -p SSH_PUBLIC_KEY="$SSH_PUBLIC_KEY" | oc apply -f -'
56 | done
57 | ___ "We created $vms VMs using our vm template" 10
58 |
59 | __ "Run demo:" 2
60 | __ "Wait for virtual machines" 3
61 | oo $((vms + 1)) "oc get vmi -n $NAMESPACE --no-headers=true --ignore-not-found=true | wc -l"
62 |
63 | __ "Wait for elasticsearch vms to be ready" 3
64 | jsonpath="{range .items[*]}{@.status.conditions[?(@.type=='Ready')].status}{'\n'}"
65 | oo $vms 'oc get pods -n '$NAMESPACE' -l app=elasticsearch,elasticsearch=master -o jsonpath="'$jsonpath'" | grep True | wc -l'
66 |
67 | __ "Wait for elasticsearch cluster to be ready" 3
68 | oo $vms "./elasticsearch/demo.sh | grep es-master | wc -l"
69 |
70 | __ "Confirm elasticsearch cluster is healthy" 3
71 | cmd ./elasticsearch/demo.sh
72 | ___ "Did $vms VMs and Coordinate Container form an Elasticsearch cluster?" 10
73 |
74 | __ "Check elasticsearch on es-master vms via RHEL container" 3
75 | for i in $(seq 0 $((vms -1))); do
76 | name=$(printf "es-master%02d" "$i")
77 | cmd 'oc rsh -n '$NAMESPACE' pod/ubi9 ssh -o StrictHostKeyChecking=accept-new elasticsearch@'$name' systemctl status elasticsearch | egrep Active -B2 --color=always'
78 | done
79 | ___ "Are all Elasticsearch services healthy?" 10
80 |
81 | __ "Cronjob logs from setting up Elasticseach Index and Kibana View" 3
82 | cmd oc logs -n aardvark -l job-name=elasticsearch-init-job
83 |
84 | __ "What did we create?" 2
85 | kinds=$(grep '\- kind' *.template.yaml -h | sort -n | uniq | sed 's/ //g' | cut -d':' -f 2 | paste -sd "," - )
86 | kindsMatch=$(echo $kinds | sed 's/,/|/g' )
87 | names='es-master|elasticsearch|data-generator|coordinate|kibana|windows2019|cockpit'
88 | oc get $kinds -l demo=ocp-virt -n $NAMESPACE | \
89 | egrep --color=always -i "^($kindsMatch)" -B10 -A10 | \
90 | GREP_COLOR='01;36' egrep --color=always $names -B10 -A10
91 |
92 | __ "Have fun storming the castle!" 1
93 | _? "Open demo urls in Chrome?" openChrome yes
94 | if [[ "$openChrome" == "yes" ]]; then
95 | /opt/google/chrome/chrome --new-window \
96 | --profile-directory=Default \
97 | https://es-master00.apps.virt.ola.purefield.nl/system/services#/elasticsearch.service?name=elastic \
98 | https://data-generator.apps.virt.ola.purefield.nl \
99 | https://kibana.apps.virt.ola.purefield.nl/app/discover\#/ \
100 | https://github.com/purefield/ocp-virt/commit/23a92611a631008ff5fe77a122f63ed34f3a8d79 \
101 | https://console-openshift-console.apps.virt.ola.purefield.nl/catalog/ns/default?category=other\&catalogType=Template \
102 | https://grafana-open-cluster-management-observability.apps.acm.ola.purefield.nl/d/WfJLo3rSz/executive-dashboards-single-cluster-view?orgId=1
103 | fi
104 |
105 | exit 0
106 | poc/demo goals
107 | - the speed to market for new applications with a tight inner and outer loop
108 | - scalability of our platform to support fluctuation in demand and states as they come online
109 | - consistency of the platform to support existing and future development
110 | - operational excellence and automation to scale the business without stretching the staff
111 |
--------------------------------------------------------------------------------
/elasticsearch/demo.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | export OK='\033[0;32mOK\033[0m'
3 | export ERROR='\033[0;31mERROR\033[0m'
4 | host=$(oc get route -l app=elasticsearch -l role=api -A -o jsonpath='{.items[].status.ingress[].host}')
5 | cmd="curl -sk https://$host/_cat/nodes"
6 | echo
7 | echo "$cmd"
8 | echo
9 | out=$($cmd 2>&1)
10 | if [[ $? -eq 0 && "x$(echo "$out" | grep es-master)" != "x" ]]; then
11 | if [ "x$(echo "$out" | grep coordinate)" == "x" ]; then
12 | echo -e "[ $ERROR ] VM cluster is up. Waiting on container"
13 | echo "$out"
14 | else
15 | echo -e "[ $OK ] Coordinate container is part of the VM cluster:"
16 | echo "$out" | sort -k9,10 | grep coordinate -A100 --color=always
17 | fi
18 | else
19 | printf "[ $ERROR ] Not ready yet.\n"
20 | fi
21 | echo
22 |
--------------------------------------------------------------------------------
/elasticsearch/generate-yaml.sh:
--------------------------------------------------------------------------------
1 | namespace=$1
2 | touch namespace.last
3 | lastNamespace=$(cat namespace.last)
4 | lastNamespace=${lastNamespace:-next-gen-virt}
5 | printf -v sshPubKey "%q" $(<../demo.id_rsa.pub tr -d '\n' | base64 -w0)
6 | if [ "x$namespace" == "x" ]
7 | then
8 | read -p "What namespace name [$lastNamespace]? " namespace
9 | fi
10 | if [ -z "$namespace" ]
11 | then
12 | namespace="$lastNamespace"
13 | fi
14 | echo "$namespace" > namespace.last
15 | source ../subscription.txt
16 | cat namespace.yaml.template | perl -pe "s/\{\{ namespace \}\}/$namespace/g" > $namespace.yaml
17 | echo "---" >> $namespace.yaml
18 | baseDomain=$(oc get --namespace openshift-ingress-operator ingresscontrollers/default -o jsonpath='{.status.domain}')
19 | if [ -n "$GUID" ]
20 | then
21 | altDomain=$baseDomain
22 | baseDomain="apps.$GUID.dynamic.opentlc.com"
23 | fi
24 | if [ -s $HOME/demo/demo.redhat.com/privkey.pem ]
25 | then
26 | oc create secret tls letsencrypt \
27 | --cert=$HOME/demo/demo.redhat.com/fullchain.pem \
28 | --key=$HOME/demo/demo.redhat.com/privkey.pem \
29 | -n $namespace --dry-run=client -o yaml >> $namespace.yaml
30 | else
31 | oc create secret generic letsencrypt \
32 | -n $namespace --dry-run=client -o yaml >> $namespace.yaml
33 | fi
34 | echo "---" >> $namespace.yaml
35 | oc create secret generic id-rsa --from-file ../demo.id_rsa -n $namespace --dry-run=client -o yaml >> $namespace.yaml
36 | cat elasticsearch.install.yaml.template kibana.yaml.template coordinate.yaml.template ubi9.yaml.template data-generator.yaml.template | \
37 | perl -pe "s/\{\{ namespace \}\}/$namespace/g" | \
38 | perl -pe "s/\{\{ baseDomain \}\}/$baseDomain/g" | \
39 | perl -pe "s/\{\{ subscriptionOrg \}\}/$subscriptionOrg/g" | \
40 | perl -pe "s/\{\{ subscriptionKey \}\}/$subscriptionKey/g" | \
41 | perl -MMIME::Base64 -pe "s/\{\{ sshPubKey \}\}/decode_base64('$sshPubKey')/ge" \
42 | >> $namespace.yaml
43 | for name in es-master00 es-master01 es-master02; do
44 | cat elasticsearch.master.vm.yaml.template | \
45 | perl -pe "s/\{\{ name \}\}/$name/g" | \
46 | perl -pe "s/\{\{ namespace \}\}/$namespace/g" | \
47 | perl -pe "s/\{\{ baseDomain \}\}/$baseDomain/g" | \
48 | perl -MMIME::Base64 -pe "s/\{\{ sshPubKey \}\}/decode_base64('$sshPubKey')/ge" \
49 | >> $namespace.yaml
50 | done
51 | cat namespace.yaml.template ../windows/vm.yaml.template | perl -pe "s/\{\{ namespace \}\}/$namespace/g" > ../windows/vm.yaml
52 | echo "# Apply yaml using:"
53 | echo "oc apply -f ../windows/vm.yaml"
54 | echo "oc apply -f $namespace.yaml"
55 | echo "watch --color ./demo.sh"
56 | echo "./kibana.data-view.sh"
57 |
--------------------------------------------------------------------------------
/elasticsearch/kibana.data-view.sh:
--------------------------------------------------------------------------------
1 | curl -kX POST "https://$(oc get route -l app=kibana -A -ojsonpath='{.items[0].spec.host}')/api/data_views/data_view" -H "kbn-xsrf: true" -H "Content-Type: application/json" -d '
2 | {
3 | "data_view": {
4 | "name":"demo",
5 | "title": "generated*",
6 | "timeFieldName": "timestamp"
7 | }
8 | }'
9 |
10 |
--------------------------------------------------------------------------------
/generate-data/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | package-lock.json
3 |
4 |
--------------------------------------------------------------------------------
/generate-data/Containerfile:
--------------------------------------------------------------------------------
1 | FROM registry.access.redhat.com/ubi8/nodejs-18
2 | USER root
3 | WORKDIR /app
4 | COPY package.json .
5 | RUN npm install
6 | USER default
7 | ENV ES_NODE=elasticsearch\
8 | ES_PORT=443 \
9 | ES_INDEX=generated \
10 | DATA_SIZE=5 \
11 | DATE_RATE=10 \
12 | DATA_BATCH=100
13 | COPY generate-data.js .
14 | COPY public public
15 | EXPOSE 3000
16 | ENTRYPOINT /usr/bin/node generate-data.js
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/generate-data/demo.js:
--------------------------------------------------------------------------------
1 | const namespace = process.env.NAMESPACE || 'next-gen-virt';
2 | const express = require('express');
3 | const port = process.env.PORT || 8080;
4 | const host = process.env.HOST;
5 |
6 | const app = express();
7 | const server = require('http').Server(app);
8 | // Handle HTTP GET request to the root URL
9 | app.get('/', (req, res) => {
10 | res.send(frames);
11 | });
12 | // Start the server
13 | server.listen(port, () => {
14 | console.log(`Server is running on port ${port}`);
15 | });
16 |
17 | let frames = `
18 |
19 |
20 |
21 | Next Gen Virt Demo
22 |
23 |
27 | `;
28 |
--------------------------------------------------------------------------------
/generate-data/generate-data.js:
--------------------------------------------------------------------------------
1 | const elasticsearch = require('@elastic/elasticsearch');
2 | const express = require('express');
3 | const socketIO = require('socket.io');
4 | const LoremIpsum = require('lorem-ipsum').LoremIpsum;
5 | const v8 = require('v8');
6 |
7 | const es_node = process.env.ES_NODE || 'coordinate';
8 | const es_port = process.env.ES_PORT || '9200';
9 | const index = process.env.ES_INDEX || 'generated';
10 | const port = process.env.UI_PORT || 3000;
11 | const secure = es_port == 443 ? 's' : '';
12 |
13 | const app = express();
14 | const server = require('http').Server(app);
15 | const io = socketIO(server);
16 | const lorem = new LoremIpsum({
17 | sentencesPerParagraph: { max: 8, min: 4 },
18 | wordsPerSentence: { max: 16, min: 4 }
19 | });
20 |
21 | var size = process.env.DATA_SIZE || 5;
22 | var rate = process.env.DATA_RATE || 10;
23 | var batch = process.env.DATA_BATCH || 100; // Number of inserts per batch
24 | var logs = true;
25 | var start = new Date().getTime();
26 | var totalBytes = 0;
27 | var deltaBytes = 0;
28 | var intervalId;
29 | var emitted = start;
30 | var randomDoc = {};
31 |
32 | // Serve static files
33 | app.use(express.static('public'));
34 |
35 | // Handle HTTP GET request to the root URL
36 | app.get('/', (req, res) => {
37 | res.sendFile(__dirname + '/public/index.html');
38 | });
39 |
40 | // Socket.IO connection
41 | io.on('connection', (socket) => {
42 | console.log('A user connected.');
43 |
44 | socket.on('updateValues', (data) => {
45 | var rateChange = data.rate != rate;
46 | size = data.size ? data.size * 1 : size;
47 | rate = data.rate ? data.rate : rate;
48 | batch = data.batch ? data.batch : batch;
49 | logs = data.logs && true;
50 | if (rateChange) updateInterval();
51 | });
52 |
53 | // Disconnect event
54 | socket.on('disconnect', () => {
55 | console.log('A user disconnected.');
56 | });
57 | });
58 |
59 | // Start the server
60 | server.listen(port, () => {
61 | console.log(`Server is running on port ${port}`);
62 | });
63 |
64 | // Elasticsearch configuration
65 | const client = new elasticsearch.Client({
66 | node: `http${secure}://${es_node}:${es_port}`,
67 | tls: {
68 | rejectUnauthorized: false
69 | }
70 | });
71 |
72 | // Update interval
73 | function updateInterval(){
74 | if (intervalId)
75 | clearInterval(intervalId);
76 | if (rate > 0) {
77 | var intervalMs = Math.round(1000 / rate);
78 | intervalId = setInterval(insertBatch, intervalMs);
79 | }
80 | }
81 |
82 | // Generate a random document
83 | function generateRandomDocument() {
84 | if (!randomDoc.data) {
85 | var data = {
86 | timestamp: new Date(),
87 | message: lorem.generateSentences(size),
88 | data: lorem.generateParagraphs(size),
89 | bytes: 0
90 | };
91 | var bytes = v8.serialize(data).length;
92 | data.bytes= bytes % 10 + bytes;
93 | randomDoc = data;
94 | }
95 | randomDoc.timestamp = new Date();
96 | return randomDoc;
97 | }
98 |
99 | // Insert batch of documents into Elasticsearch
100 | async function insertBatch() {
101 | const documents = Array.from({ length: batch }, generateRandomDocument);
102 | const body = documents.flatMap((doc) => [{ index: { _index: index } }, doc]);
103 |
104 | try {
105 | const response = await client.bulk({ body });
106 | if (response.errors) {
107 | const errorItems = response.items.filter((item) => item.index && item.index.error);
108 | console.error('Error inserting documents:', errorItems);
109 | } else {
110 | var bytes = v8.serialize(body).length;
111 | var duration = new Date().getTime() - start;
112 | totalBytes += bytes;
113 | deltaBytes += bytes;
114 | bytes = humanBytes(bytes);
115 | var msg = `Inserted ${batch} documents ${bytes} into ${index} on ${es_node}:${es_port} (s:${size} r:${rate})`
116 | var now = new Date().getTime();
117 | var data = {
118 | bytes: humanBytes(totalBytes),
119 | avg: humanBytes(parseInt(totalBytes/(duration/1000/60))),
120 | rate: humanBytes(parseInt(deltaBytes/((now-emitted)/1000)))
121 | };
122 | if (logs) {
123 | console.log(msg);
124 | data.log = msg;
125 | }
126 | if (now - emitted > 300){
127 | io.emit('data', data);
128 | emitted = now;
129 | deltaBytes = 0;
130 | }
131 | }
132 | } catch (error) {
133 | console.error('Error inserting documents:', error);
134 | }
135 | }
136 |
137 | function humanBytes(size) {
138 | var i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
139 | return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
140 | }
141 |
142 | updateInterval();
143 |
--------------------------------------------------------------------------------
/generate-data/mappings.curl:
--------------------------------------------------------------------------------
1 | curl -kXPUT -H "Content-Type: application/json" https://$host/_template/demo -d'
2 | {
3 | "index_patterns": [
4 | "generated*"
5 | ],
6 | "settings": {
7 | "number_of_shards": 2,
8 | "number_of_replicas": 1
9 | },
10 | "mappings": {
11 | "properties": {
12 | "@timestamp": {
13 | "type": "date"
14 | },
15 | "@version": {
16 | "type": "text",
17 | "fields": {
18 | "keyword": {
19 | "type": "keyword"
20 | }
21 | }
22 | },
23 | "bytes": {
24 | "type": "integer"
25 | },
26 | "message": {
27 | "type": "text",
28 | "fields": {
29 | "keyword": {
30 | "type": "keyword"
31 | }
32 | }
33 | },
34 | "data": {
35 | "type": "text",
36 | "fields": {
37 | "keyword": {
38 | "type": "keyword"
39 | }
40 | }
41 | }
42 | }
43 | }
44 | }'
45 |
--------------------------------------------------------------------------------
/generate-data/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generate-data",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "generate-data.js",
6 | "dependencies": {
7 | "@elastic/elasticsearch": "^8.13.1",
8 | "express": "^4.18.2",
9 | "lorem-ipsum": "^2.0.8",
10 | "morgan": "^1.10.0",
11 | "socket.io": "^4.6.1"
12 | },
13 | "devDependencies": {},
14 | "scripts": {
15 | "test": "echo \"Error: no test specified\" && exit 1",
16 | "start": "node generate-data.js"
17 | },
18 | "author": "",
19 | "license": "ISC"
20 | }
21 |
--------------------------------------------------------------------------------
/generate-data/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Log Streamer
6 |
11 |
12 |
41 |
42 |
43 |
44 | Data Generator
45 | total
46 | /min/avg
47 | /sec
48 |
49 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/generate-data/rebuild.sh:
--------------------------------------------------------------------------------
1 | host=$(oc get route -l app=elasticsearch -l role=coordinate -A -o jsonpath='{.items[].status.ingress[].host}')
2 | podman kill generate-data
3 | podman build -t generate-data .
4 | ./mappings.curl $host
5 | podman run -it --rm -p 3000:3000 -e ES_NODE=$host -e ES_PORT=443 -e DATA_RATE=100 -e DATA_SIZE=5 -e DATA_BATCH=100 --name generate-data generate-data:latest
6 |
--------------------------------------------------------------------------------
/hybrid-virt-elasticsearch.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/purefield/ocp-virt/56710f72b38e93299c396df5efe1ce0c8477ef98/hybrid-virt-elasticsearch.webp
--------------------------------------------------------------------------------
/net.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: nmstate.io/v1
3 | kind: NodeNetworkConfigurationPolicy
4 | metadata:
5 | name: bond0.777
6 | spec:
7 | desiredState:
8 | interfaces:
9 | - name: bond0.777
10 | type: vlan
11 | state: up
12 | vlan:
13 | base-iface: bond0
14 | id: 777
15 | ipv4:
16 | enabled: false
17 | dhcp: false
18 | ipv6:
19 | enabled: false
20 | dhcp: false
21 | ---
22 | apiVersion: "k8s.cni.cncf.io/v1"
23 | kind: NetworkAttachmentDefinition
24 | metadata:
25 | name: vlan-777-bond0
26 | namespace: engineering-vms
27 | spec:
28 | config: '{
29 | "cniVersion": "0.3.1",
30 | "name": "vlan-777-bond0",
31 | "type": "cnv-bridge",
32 | "master": "bond0.777",
33 | "macspoofchk": false,
34 | "preserveDefaultVlan": false,
35 | "ipam": {
36 | "type": "whereabouts",
37 | "range": "192.168.99.0/24"
38 | }
39 | }'
40 |
--------------------------------------------------------------------------------
/rbac.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: user.openshift.io/v1
3 | kind: Group
4 | metadata:
5 | name: engineering
6 | users:
7 | - therevoman
8 | - daniel
9 | - thomasphall
10 | ---
11 | kind: Project
12 | apiVersion: project.openshift.io/v1
13 | metadata:
14 | name: engineering-vms
15 | ---
16 | kind: RoleBinding
17 | apiVersion: rbac.authorization.k8s.io/v1
18 | metadata:
19 | name: engineering-vms-user
20 | namespace: engineering-vms
21 | subjects:
22 | - kind: Group
23 | apiGroup: rbac.authorization.k8s.io
24 | name: engineering
25 | - kind: Group
26 | apiGroup: rbac.authorization.k8s.io
27 | name: engineering-admin
28 | roleRef:
29 | apiGroup: rbac.authorization.k8s.io
30 | kind: ClusterRole
31 | name: edit
32 | ---
33 | kind: RoleBinding
34 | apiVersion: rbac.authorization.k8s.io/v1
35 | metadata:
36 | name: engineering-vms-admin
37 | namespace: engineering-vms
38 | subjects:
39 | - kind: Group
40 | apiGroup: rbac.authorization.k8s.io
41 | name: engineering-admin
42 | roleRef:
43 | apiGroup: rbac.authorization.k8s.io
44 | kind: ClusterRole
45 | name: admin
46 | ---
47 | kind: Role
48 | apiVersion: rbac.authorization.k8s.io/v1
49 | metadata:
50 | name: vm-templates-engineering-users
51 | namespace: engineering-vms
52 | rules:
53 | - verbs:
54 | - get
55 | - watch
56 | - list
57 | - create
58 | apiGroups:
59 | - processedtemplates.template.openshift.io
60 | - template.openshift.io
61 | resources:
62 | - processedtemplates
63 | - verbs:
64 | - get
65 | - watch
66 | - list
67 | apiGroups:
68 | - k8s.cni.cncf.io
69 | resources:
70 | - network-attachment-definitions
71 | ---
72 | kind: RoleBinding
73 | apiVersion: rbac.authorization.k8s.io/v1
74 | metadata:
75 | name: role-binding-engineering-group-vm-templates
76 | namespace: engineering-vms
77 | subjects:
78 | - kind: Group
79 | apiGroup: rbac.authorization.k8s.io
80 | name: engineering
81 | - kind: Group
82 | apiGroup: rbac.authorization.k8s.io
83 | name: engineering-admin
84 | roleRef:
85 | apiGroup: rbac.authorization.k8s.io
86 | kind: Role
87 | name: vm-templates-engineering-users
88 |
--------------------------------------------------------------------------------
/setup.template.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: template.openshift.io/v1
3 | kind: Template
4 | metadata:
5 | name: ocp-virt-demo-setup-template
6 | annotations:
7 | iconClass: "icon-elastic"
8 | tags: "elasticsearch,virtualization"
9 | template.openshift.io/bindable: "true"
10 | openshift.io/display-name: OpenShift Virtualization Demo - 1/2 Environment Setup
11 | openshift.io/documentation-url: https://github.com/purefield/ocp-virt
12 | description: "OpenShift Virtualization and Container Application Stack Demo.
13 | This template provides a demo application stack around Elasticsearch.
14 |
15 | Bringing VMs and Containers together.
16 |
17 | It includes Elasticsearch VMs, Kibana, data generation application and coordinator containers."
18 | openshift.io/provider-display-name: Daniel Schimpfoessl
19 | objects:
20 | - kind: Namespace
21 | apiVersion: v1
22 | metadata:
23 | name: ${NAMESPACE}
24 | - kind: Secret
25 | apiVersion: v1
26 | metadata:
27 | name: letsencrypt
28 | namespace: ${NAMESPACE}
29 | - kind: Secret
30 | apiVersion: v1
31 | metadata:
32 | name: id-rsa
33 | namespace: ${NAMESPACE}
34 | stringData:
35 | demo.id_rsa: ${SSH_PRIVATE_KEY}
36 | - kind: ConfigMap
37 | apiVersion: v1
38 | metadata:
39 | name: install-scripts
40 | namespace: ${NAMESPACE}
41 | data:
42 | elasticsearch.sh: |-
43 | subscription-manager register --activationkey ${SUBSCRIPTION_KEY} --org ${SUBSCRIPTION_ORG}
44 | subscription-manager attach --auto
45 | cat << EOF > /etc/yum.repos.d/elastic.repo
46 | [elasticsearch]
47 | name=Elasticsearch repository for 8.x packages
48 | baseurl=https://artifacts.elastic.co/packages/8.x/yum
49 | gpgcheck=1
50 | gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
51 | enabled=0
52 | autorefresh=1
53 | type=rpm-md
54 | EOF
55 | update-crypto-policies --set DEFAULT:SHA1
56 | rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
57 | dnf install -y --enablerepo=elasticsearch elasticsearch-8.7.0
58 | update-crypto-policies --set DEFAULT
59 | systemctl daemon-reload
60 | mkdir -p /srv/elasticsearch/config
61 | mkdir -p /srv/elasticsearch/data
62 | chown elasticsearch:elasticsearch /srv/elasticsearch -R
63 | mkfs.xfs /dev/disk/by-id/virtio-datadisk
64 | config=$(ls -1 /dev/disk/by-id/virtio-00yaml)
65 | mount $config /srv/elasticsearch/config
66 | mount /dev/disk/by-id/virtio-datadisk /srv/elasticsearch/data
67 | echo "$config /srv/elasticsearch/config iso9660 defaults 0 0" >> /etc/fstab
68 | echo '/dev/disk/by-id/virtio-datadisk /srv/elasticsearch/data xfs defaults 0 0' >> /etc/fstab
69 | mv /etc/elasticsearch/elasticsearch.yml /etc/elasticsearch/elasticsearch.yml.org
70 | mv /etc/elasticsearch/jvm.options /etc/elasticsearch/jvm.options.org
71 | ln -sf /srv/elasticsearch/config/elasticsearch.conf /etc/elasticsearch/elasticsearch.yml
72 | ln -sf /srv/elasticsearch/config/jvm.options /etc/elasticsearch/jvm.options
73 | systemctl enable elasticsearch --now
74 | system.sh: |-
75 | cert=$(ls -1 /dev/disk/by-id/virtio-00cert)
76 | mount $cert /etc/cockpit/ws-certs.d/
77 | if [ -f "/etc/cockpit/ws-certs.d/tls.crt" ]; then echo "$cert /etc/cockpit/ws-certs.d/ xfs defaults 0 0" >> /etc/fstab;
78 | else umount $cert; fi
79 | systemctl enable cockpit.socket --now
80 | immutable: false
81 | - kind: Service
82 | apiVersion: v1
83 | metadata:
84 | name: es-master-svc
85 | namespace: ${NAMESPACE}
86 | spec:
87 | ports:
88 | - name: elasticsearch
89 | protocol: TCP
90 | port: 9200
91 | targetPort: 9200
92 | selector:
93 | app: elasticsearch
94 | elasticsearch: master
95 | - kind: Route
96 | apiVersion: route.openshift.io/v1
97 | metadata:
98 | name: elasticsearch
99 | namespace: ${NAMESPACE}
100 | annotations:
101 | cert-utils-operator.redhat-cop.io/certs-from-secret: letsencrypt
102 | labels:
103 | app: elasticsearch
104 | role: api
105 | spec:
106 | host: elasticsearch.${BASEDOMAIN}
107 | to:
108 | kind: Service
109 | name: es-master-svc
110 | port:
111 | targetPort: elasticsearch
112 | tls:
113 | termination: edge
114 | insecureEdgeTerminationPolicy: Redirect
115 | wildcardPolicy: None
116 | - kind: Deployment
117 | apiVersion: apps/v1
118 | metadata:
119 | name: kibana
120 | namespace: ${NAMESPACE}
121 | annotations:
122 | app.openshift.io/connects-to: '[{"apiVersion":"apps/v1","kind":"Deployment","name":"coordinate"}]'
123 | labels:
124 | app.kubernetes.io/part-of: kibana
125 | app.openshift.io/runtime: elastic
126 | app.openshift.io/name: kibana
127 | spec:
128 | replicas: 1
129 | selector:
130 | matchLabels:
131 | app: kibana
132 | template:
133 | metadata:
134 | labels:
135 | app: kibana
136 | spec:
137 | # securityContext:
138 | # seccompProfile:
139 | # type: RuntimeDefault
140 | containers:
141 | - name: kibana
142 | image: docker.elastic.co/kibana/kibana:8.7.0
143 | ports:
144 | - containerPort: 5601
145 | # securityContext:
146 | # allowPrivilegeEscalation: false
147 | # runAsNonRoot: true
148 | # capabilities:
149 | # drop:
150 | # - ALL
151 | env:
152 | - name: ELASTICSEARCH_HOSTS
153 | value: http://coordinate:9200
154 | - name: SERVER_PUBLICBASEURL
155 | value: https://kibana-demo.${BASEDOMAIN}
156 | - name: SERVER_NAME
157 | value: kibana-demo.${BASEDOMAIN}
158 | resources: {}
159 | restartPolicy: Always
160 | - kind: Service
161 | apiVersion: v1
162 | metadata:
163 | name: kibana
164 | namespace: ${NAMESPACE}
165 | spec:
166 | ports:
167 | - name: ui
168 | port: 5601
169 | targetPort: 5601
170 | selector:
171 | app: kibana
172 | - kind: Route
173 | apiVersion: route.openshift.io/v1
174 | metadata:
175 | name: kibana
176 | namespace: ${NAMESPACE}
177 | labels:
178 | app: kibana
179 | annotations:
180 | cert-utils-operator.redhat-cop.io/certs-from-secret: letsencrypt
181 | spec:
182 | host: kibana.${BASEDOMAIN}
183 | to:
184 | kind: Service
185 | name: kibana
186 | port:
187 | targetPort: ui
188 | tls:
189 | termination: edge
190 | insecureEdgeTerminationPolicy: Redirect
191 | wildcardPolicy: None
192 | - kind: Service
193 | apiVersion: v1
194 | metadata:
195 | name: coordinate
196 | namespace: ${NAMESPACE}
197 | spec:
198 | ports:
199 | - name: elasticsearch
200 | port: 9200
201 | targetPort: 9200
202 | - name: elasticsearch-discovery
203 | port: 9300
204 | targetPort: 9300
205 | selector:
206 | app: elasticsearch
207 | elasticsearch: coordinate
208 | - kind: Deployment
209 | apiVersion: apps/v1
210 | metadata:
211 | name: coordinate
212 | namespace: ${NAMESPACE}
213 | annotations:
214 | app.openshift.io/connects-to: '[{"apiVersion":"kubevirt.io/v1","kind":"VirtualMachine","name":"es-master00"},{"apiVersion":"kubevirt.io/v1","kind":"VirtualMachine","name":"es-master01"},{"apiVersion":"kubevirt.io/v1","kind":"VirtualMachine","name":"es-master02"}]'
215 | labels:
216 | app.kubernetes.io/part-of: elasticsearch
217 | app.openshift.io/runtime: elastic
218 | app.openshift.io/name: coordinate
219 | spec:
220 | replicas: 1
221 | selector:
222 | matchLabels:
223 | app: elasticsearch
224 | elasticsearch: coordinate
225 | template:
226 | metadata:
227 | labels:
228 | app: elasticsearch
229 | elasticsearch: coordinate
230 | spec:
231 | # securityContext:
232 | # seccompProfile:
233 | # type: RuntimeDefault
234 | containers:
235 | - name: coordinate
236 | image: docker.elastic.co/elasticsearch/elasticsearch:8.7.0-amd64
237 | ports:
238 | - containerPort: 9200
239 | - containerPort: 9300
240 | # securityContext:
241 | # allowPrivilegeEscalation: false
242 | # runAsNonRoot: true
243 | # capabilities:
244 | # drop:
245 | # - ALL
246 | env:
247 | - name: ES_JAVA_OPTS
248 | value: -Xms8g -Xmx8g
249 | - name: node.name
250 | value: coordinate
251 | - name: cluster.name
252 | value: demo
253 | - name: xpack.security.enabled
254 | value: "false"
255 | - name: xpack.security.transport.ssl.enabled
256 | value: "false"
257 | - name: xpack.security.http.ssl.enabled
258 | value: "false"
259 | - name: discovery.seed_hosts
260 | value: es-master00,es-master01,es-master02
261 | - name: node.roles
262 | value: "[]"
263 | - name: network.publish_host
264 | value: coordinate.${NAMESPACE}.svc.cluster.local
265 | - name: network.host
266 | value: _site_
267 | resources: {}
268 | restartPolicy: Always
269 | - kind: Route
270 | apiVersion: route.openshift.io/v1
271 | metadata:
272 | name: coordinate
273 | namespace: ${NAMESPACE}
274 | annotations:
275 | cert-utils-operator.redhat-cop.io/certs-from-secret: letsencrypt
276 | labels:
277 | app: elasticsearch
278 | role: coordinate
279 | spec:
280 | host: coordinate.${BASEDOMAIN}
281 | to:
282 | kind: Service
283 | name: coordinate
284 | port:
285 | targetPort: elasticsearch
286 | tls:
287 | termination: edge
288 | insecureEdgeTerminationPolicy: Redirect
289 | wildcardPolicy: None
290 | - kind: ConfigMap
291 | apiVersion: v1
292 | metadata:
293 | name: ubi9-install
294 | namespace: ${NAMESPACE}
295 | data:
296 | install.sh: |-
297 | mkdir /root/.ssh/
298 | cp /mnt/demo.id_rsa /root/.ssh/id_rsa
299 | chmod 700 /root/.ssh/
300 | chmod 0400 /root/.ssh/id_rsa
301 | yum install -y openssh-clients rsync
302 | /bin/sh
303 | immutable: false
304 | - kind: Pod
305 | apiVersion: v1
306 | metadata:
307 | name: ubi9
308 | annotations:
309 | app.openshift.io/connects-to: '[{"apiVersion":"apps/v1","kind":"Deployment","name":"kibana"}]'
310 | labels:
311 | app: ubi9
312 | app.kubernetes.io/part-of: utility
313 | app.openshift.io/runtime: redhat
314 | app.openshift.io/name: utility
315 | namespace: ${NAMESPACE}
316 | spec:
317 | volumes:
318 | - configMap:
319 | name: ubi9-install
320 | name: ubi9-install
321 | - secret:
322 | secretName: id-rsa
323 | name: id-rsa
324 | securityContext:
325 | seLinuxOptions:
326 | level: 's0:c31,c10'
327 | seccompProfile:
328 | type: "RuntimeDefault"
329 | containers:
330 | - name: ubi9
331 | image: 'registry.access.redhat.com/ubi9/ubi'
332 | command: ["/bin/sh"]
333 | args: ["/usr/local/bin/install.sh"]
334 | stdin: true
335 | tty: true
336 | volumeMounts:
337 | - name: ubi9-install
338 | readOnly: true
339 | mountPath: /usr/local/bin/install.sh
340 | subPath: install.sh
341 | - name: id-rsa
342 | readOnly: true
343 | mountPath: /mnt/
344 | - kind: ImageStream
345 | apiVersion: image.openshift.io/v1
346 | metadata:
347 | name: data-generator-imagestream
348 | namespace: ${NAMESPACE}
349 | - kind: BuildConfig
350 | apiVersion: build.openshift.io/v1
351 | metadata:
352 | annotations:
353 | app.openshift.io/vcs-ref: ''
354 | app.openshift.io/vcs-uri: 'https://github.com/purefield/ocp-virt'
355 | openshift.io/generated-by: OpenShiftWebConsole
356 | name: data-generator-buildconfig
357 | namespace: ${NAMESPACE}
358 | labels:
359 | app: data-generator
360 | app.kubernetes.io/component: data-generator
361 | app.kubernetes.io/instance: data-generator
362 | app.kubernetes.io/name: data-generator
363 | app.kubernetes.io/part-of: application
364 | app.openshift.io/runtime: nodejs
365 | app.openshift.io/runtime-version: 18-ubi9-minimal
366 | spec:
367 | output:
368 | to:
369 | kind: ImageStreamTag
370 | name: 'data-generator-imagestream:latest'
371 | resources: {}
372 | successfulBuildsHistoryLimit: 2
373 | failedBuildsHistoryLimit: 2
374 | strategy:
375 | type: Source
376 | sourceStrategy:
377 | from:
378 | kind: ImageStreamTag
379 | namespace: openshift
380 | name: 'nodejs:18-ubi9-minimal'
381 | postCommit: {}
382 | source:
383 | type: Git
384 | git:
385 | uri: 'https://github.com/purefield/ocp-virt'
386 | contextDir: /generate-data
387 | triggers:
388 | - type: Generic
389 | generic:
390 | secretReference:
391 | name: data-generator-generic-webhook-secret
392 | - type: ConfigChange
393 | runPolicy: Serial
394 | - kind: Deployment
395 | apiVersion: apps/v1
396 | metadata:
397 | name: data-generator
398 | namespace: ${NAMESPACE}
399 | labels:
400 | app: data-generator
401 | app.kubernetes.io/component: data-generator
402 | app.kubernetes.io/instance: data-generator
403 | app.kubernetes.io/name: data-generator
404 | app.kubernetes.io/part-of: application
405 | app.openshift.io/runtime: nodejs
406 | app.openshift.io/runtime-version: 18-ubi9-minimal
407 | annotations:
408 | app.openshift.io/connects-to: '[{"apiVersion":"apps/v1","kind":"Deployment","name":"coordinate"}]'
409 | spec:
410 | replicas: 1
411 | selector:
412 | matchLabels:
413 | app: data-generator
414 | template:
415 | metadata:
416 | labels:
417 | app: data-generator
418 | deployment: data-generator
419 | spec:
420 | containers:
421 | - name: data-generator
422 | stdin: true
423 | tty: true
424 | image: >-
425 | image-registry.openshift-image-registry.svc:5000/${NAMESPACE}/data-generator-imagestream
426 | ports:
427 | - containerPort: 3000
428 | protocol: TCP
429 | env:
430 | - name: ES_NODE
431 | value: coordinate
432 | - name: ES_PORT
433 | value: '9200'
434 | - name: ES_INDEX
435 | value: generated
436 | - name: DATA_SIZE
437 | value: '5'
438 | - name: DATE_RATE
439 | value: '10'
440 | - name: DATA_BATCH
441 | value: '100'
442 | resources: {}
443 | imagePullPolicy: Always
444 | restartPolicy: Always
445 | terminationGracePeriodSeconds: 30
446 | dnsPolicy: ClusterFirst
447 | securityContext: {}
448 | schedulerName: default-scheduler
449 | strategy:
450 | type: RollingUpdate
451 | rollingUpdate:
452 | maxUnavailable: 50%
453 | maxSurge: 25%
454 | revisionHistoryLimit: 2
455 | progressDeadlineSeconds: 60
456 | - kind: Service
457 | apiVersion: v1
458 | metadata:
459 | name: data-generator
460 | namespace: ${NAMESPACE}
461 | spec:
462 | selector:
463 | app: data-generator
464 | ports:
465 | - protocol: TCP
466 | port: 3000
467 | targetPort: 3000
468 | - kind: Route
469 | apiVersion: route.openshift.io/v1
470 | metadata:
471 | name: data-generator
472 | namespace: ${NAMESPACE}
473 | labels: {}
474 | spec:
475 | host: data-generator.${BASEDOMAIN}
476 | to:
477 | kind: Service
478 | name: data-generator
479 | tls:
480 | termination: edge
481 | insecureEdgeTerminationPolicy: Redirect
482 | destinationCACertificate: ''
483 | port:
484 | targetPort: 3000
485 | - kind: ConfigMap
486 | apiVersion: v1
487 | metadata:
488 | name: windows-vm-sysprep
489 | namespace: ${NAMESPACE}
490 | data:
491 | Autounattend.xml: >
492 |
493 |
494 |
498 |
499 |
500 |
501 |
502 |
503 |
504 | 1
505 | true
506 | Primary
507 |
508 |
509 |
510 |
511 | true
512 | NTFS
513 |
514 | 1
515 | 1
516 |
517 |
518 | 0
519 | true
520 |
521 |
522 |
523 |
524 |
525 |
526 | /IMAGE/NAME
527 | Windows Server 2019 SERVERSTANDARD
528 |
529 |
530 |
531 | 0
532 | 1
533 |
534 |
535 |
536 |
537 | true
538 | Administrator
539 | My Organization
540 |
541 | false
542 |
543 |
544 |
545 | en-US
546 |
547 | en-US
548 | en-US
549 | en-US
550 | en-US
551 |
552 |
553 |
554 |
555 | false
556 |
557 |
558 |
559 |
560 |
561 |
562 | R3dh4t1!
563 | true
564 |
565 | true
566 | 999
567 | Administrator
568 |
569 |
570 | true
571 | true
572 | true
573 | true
574 | Work
575 | 3
576 | true
577 |
578 |
579 |
580 |
581 | Local Administrator Account
582 | Administrator
583 | Administrators
584 | Administrator
585 |
586 |
587 |
588 | Eastern Standard Time
589 |
590 |
591 |
592 |
593 | en-US
594 | en-US
595 | en-US
596 | en-US
597 |
598 |
599 |
600 |
601 | R3dh4t1!
602 | true
603 |
604 | true
605 | 999
606 | Administrator
607 |
608 |
609 | true
610 | true
611 | true
612 | true
613 | Work
614 | 3
615 | true
616 |
617 |
618 |
619 |
620 | Local Administrator Account
621 | Administrator
622 | Administrators
623 | Administrator
624 |
625 |
626 |
627 | Eastern Standard Time
628 |
629 |
630 |
631 | - kind: VirtualMachine
632 | apiVersion: kubevirt.io/v1
633 | metadata:
634 | name: windows2019
635 | namespace: ${NAMESPACE}
636 | labels:
637 | app: windows
638 | vm.kubevirt.io/template: windows2k19-server-medium
639 | vm.kubevirt.io/template.namespace: openshift
640 | app.kubernetes.io/part-of: application
641 | annotations:
642 | app.openshift.io/connects-to: '[{"apiVersion":"apps/v1","kind":"Deployment","name":"coordinate"}]'
643 | spec:
644 | dataVolumeTemplates:
645 | - apiVersion: cdi.kubevirt.io/v1beta1
646 | kind: DataVolume
647 | metadata:
648 | annotations:
649 | cdi.kubevirt.io/storage.bind.immediate.requested: 'true'
650 | creationTimestamp: null
651 | name: windows
652 | spec:
653 | source:
654 | blank: {}
655 | storage:
656 | resources:
657 | requests:
658 | storage: 60Gi
659 | - metadata:
660 | creationTimestamp: null
661 | name: windows-installation-cdrom
662 | spec:
663 | source:
664 | pvc:
665 | name: windows-2019
666 | namespace: openshift-virtualization-os-images
667 | storage:
668 | resources:
669 | requests:
670 | storage: 5Gi
671 | running: true
672 | template:
673 | metadata:
674 | annotations:
675 | vm.kubevirt.io/flavor: medium
676 | vm.kubevirt.io/os: windows2k19
677 | vm.kubevirt.io/workload: server
678 | creationTimestamp: null
679 | labels:
680 | kubevirt.io/domain: windows
681 | kubevirt.io/size: medium
682 | spec:
683 | architecture: amd64
684 | domain:
685 | clock:
686 | timer:
687 | hpet:
688 | present: false
689 | hyperv: {}
690 | pit:
691 | tickPolicy: delay
692 | rtc:
693 | tickPolicy: catchup
694 | utc: {}
695 | cpu:
696 | cores: 1
697 | sockets: 1
698 | threads: 1
699 | devices:
700 | disks:
701 | - bootOrder: 2
702 | disk:
703 | bus: sata
704 | name: rootdisk
705 | - bootOrder: 1
706 | cdrom:
707 | bus: sata
708 | name: installation-cdrom
709 | - cdrom:
710 | bus: sata
711 | name: windows-drivers-disk
712 | - cdrom:
713 | bus: sata
714 | name: sysprep
715 | inputs:
716 | - bus: usb
717 | name: tablet
718 | type: tablet
719 | interfaces:
720 | - masquerade: {}
721 | model: e1000e
722 | name: default
723 | memory:
724 | guest: 4Gi
725 | networks:
726 | - name: default
727 | pod: {}
728 | terminationGracePeriodSeconds: 3600
729 | volumes:
730 | - dataVolume:
731 | name: windows
732 | name: rootdisk
733 | - dataVolume:
734 | name: windows-installation-cdrom
735 | name: installation-cdrom
736 | - containerDisk:
737 | image: >-
738 | registry.redhat.io/container-native-virtualization/virtio-win-rhel9
739 | name: windows-drivers-disk
740 | - name: sysprep
741 | sysprep:
742 | configMap:
743 | name: windows-vm-sysprep
744 | - kind: Job
745 | apiVersion: batch/v1
746 | metadata:
747 | name: elasticsearch-init-job
748 | namespace: ${NAMESPACE}
749 | spec:
750 | ttlSecondsAfterFinished: 30
751 | template:
752 | metadata:
753 | name: elasticsearch-init
754 | spec:
755 | restartPolicy: Never
756 | containers:
757 | - name: es-seed
758 | image: curlimages/curl:latest # Using a lightweight Curl image
759 | command:
760 | - "/bin/sh"
761 | - "-c"
762 | - |
763 | echo "Waiting for Elasticsearch to be ready..."
764 | until curl -LfX GET -so /dev/null http://coordinate:9200/_cluster/health; do
765 | echo "Not ready yet, sleeping for 5 sec"
766 | sleep 5;
767 | done
768 |
769 | if curl -Lfso /dev/null http://coordinate:9200/_index_template/default_template; then
770 | echo "Default Template already setup, exiting"
771 | exit 0
772 | fi
773 |
774 | echo "Creating the default index template..."
775 | curl -sX PUT "http://coordinate:9200/_index_template/default_template" -H "Content-Type: application/json" -d '
776 | {
777 | "index_patterns": ["generated"],
778 | "template": {
779 | "settings": {
780 | "number_of_shards": '${SHARDS}',
781 | "number_of_replicas": 1
782 | },
783 | "mappings": {
784 | "properties": {
785 | "timestamp": { "type": "date" },
786 | "message": { "type": "text" }
787 | }
788 | }
789 | }
790 | }'
791 | echo; echo;
792 |
793 | echo "Deleting the 'generated' index..."
794 | curl -sX DELETE "http://coordinate:9200/generated"
795 | echo; echo;
796 |
797 | echo "Elasticsearch index template setup complete."
798 | echo
799 |
800 | echo "Waiting for Kibana to be ready..."
801 | until curl -LfX GET -so /dev/null http://kibana:5601/api/status; do
802 | echo "Not ready yet, sleeping for 5 sec"
803 | sleep 5;
804 | done
805 |
806 | echo "Create Kibana data view"
807 | curl -sX POST "http://kibana:5601/api/data_views/data_view" -H "kbn-xsrf: true" -H "Content-Type: application/json" -d '
808 | {
809 | "data_view": {
810 | "name":"demo",
811 | "title": "generated*",
812 | "timeFieldName": "timestamp"
813 | }
814 | }'
815 | echo; echo;
816 |
817 | echo "Kibana view setup complete."
818 |
819 | parameters:
820 | - name: NAMESPACE
821 | description: Namespace to use
822 | required: true
823 | - name: BASEDOMAIN
824 | description: Base Domain for externally exposed services
825 | required: true
826 | - name: SHARDS
827 | description: Default number of shards per index
828 | value: "3"
829 | - name: SUBSCRIPTION_ORG
830 | description: Red Hat Subscription ORG string
831 | required: true
832 | - name: SUBSCRIPTION_KEY
833 | description: Red Hat Subscription KEY string
834 | required: true
835 | - name: SSH_PUBLIC_KEY
836 | description: SSH Public Key
837 | required: true
838 | - name: SSH_PRIVATE_KEY
839 | description: SSH Private Key
840 | required: true
841 | labels:
842 | demo: ocp-virt
843 |
--------------------------------------------------------------------------------
/vm.template.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: template.openshift.io/v1
3 | kind: Template
4 | metadata:
5 | name: ocp-virt-demo-vms-template
6 | annotations:
7 | iconClass: "icon-elastic"
8 | tags: "elasticsearch,virtualization"
9 | template.openshift.io/bindable: "true"
10 | openshift.io/display-name: OpenShift Virtualization Demo - 2/2 ElasticSearch VM
11 | openshift.io/documentation-url: https://github.com/purefield/ocp-virt
12 | description: "OpenShift Virtualization and Container Application Stack Demo.
13 | This template provides virtual machines running Elasticsearch.
14 |
15 | Bringing VMs and Containers together.
16 |
17 | It includes Elasticsearch VMs, Kibana, data generation application and coordinator containers."
18 | openshift.io/provider-display-name: Daniel Schimpfoessl
19 | objects:
20 | - kind: ConfigMap
21 | apiVersion: v1
22 | metadata:
23 | name: ${VMNAME}-conf
24 | namespace: ${NAMESPACE}
25 | data:
26 | elasticsearch.conf: |-
27 | cluster.name: demo
28 | node.name: ${VMNAME}
29 | node.roles: [ master, data ]
30 | network.host: _site_
31 | network.publish_host: ${VMNAME}.${NAMESPACE}.svc.cluster.local
32 | discovery.seed_hosts: ["es-master00","es-master01","es-master02"]
33 | cluster.initial_master_nodes: ["es-master00","es-master01","es-master02"]
34 | path.data: /srv/elasticsearch
35 | path.logs: /var/log/elasticsearch
36 | xpack.security.enabled: false
37 | xpack.security.transport.ssl.enabled: false
38 | xpack.security.http.ssl.enabled: false
39 | jvm.options: |-
40 | -Xms4096m
41 | -Xmx4096m
42 | -XX:+DisableExplicitGC
43 | -XX:+AlwaysPreTouch
44 | -server
45 | -Djava.awt.headless=true
46 | -Dfile.encoding=UTF-8
47 | -Djna.nosys=true
48 | -Djdk.io.permissionsUseCanonicalPath=true
49 | -Dio.netty.noUnsafe=true
50 | -Dio.netty.noKeySetOptimization=true
51 | -Dlog4j.shutdownHookEnabled=false
52 | -Dlog4j2.disable.jmx=true
53 | -Dlog4j.skipJansi=true
54 | -XX:+HeapDumpOnOutOfMemoryError
55 | immutable: false
56 | - kind: Service
57 | apiVersion: v1
58 | metadata:
59 | name: ${VMNAME}
60 | namespace: ${NAMESPACE}
61 | annotations:
62 | service.beta.openshift.io/serving-cert-secret-name: ssl-cert-cockpit-${VMNAME}
63 | spec:
64 | selector:
65 | node: ${VMNAME}
66 | ports:
67 | - name: elasticsearch
68 | protocol: TCP
69 | port: 9200
70 | targetPort: 9200
71 | - name: elasticsearch-discovery
72 | protocol: TCP
73 | port: 9300
74 | targetPort: 9300
75 | - name: ssh
76 | protocol: TCP
77 | port: 22
78 | targetPort: 22
79 | - name: cockpit
80 | protocol: TCP
81 | port: 9090
82 | targetPort: 9090
83 | - kind: VirtualMachine
84 | apiVersion: kubevirt.io/v1
85 | metadata:
86 | name: ${VMNAME}
87 | namespace: ${NAMESPACE}
88 | labels:
89 | app.kubernetes.io/part-of: elasticsearch
90 | spec:
91 | dataVolumeTemplates:
92 | - metadata:
93 | creationTimestamp: null
94 | name: ${VMNAME}-data
95 | namespace: ${NAMESPACE}
96 | spec:
97 | preallocation: false
98 | source:
99 | blank: {}
100 | storage:
101 | resources:
102 | requests:
103 | storage: 80Gi
104 | - metadata:
105 | creationTimestamp: null
106 | name: ${VMNAME}
107 | namespace: ${NAMESPACE}
108 | spec:
109 | preallocation: false
110 | sourceRef:
111 | kind: DataSource
112 | name: rhel9
113 | namespace: openshift-virtualization-os-images
114 | storage:
115 | resources:
116 | requests:
117 | storage: 30Gi
118 | volumeMode: Block
119 | running: true
120 | template:
121 | metadata:
122 | annotations:
123 | vm.kubevirt.io/os: rhel9
124 | vm.kubevirt.io/workload: server
125 | labels:
126 | app: elasticsearch
127 | node: ${VMNAME}
128 | elasticsearch: master
129 | spec:
130 | readinessProbe:
131 | httpGet:
132 | port: 9200
133 | path: /
134 | initialDelaySeconds: 90
135 | timeoutSeconds: 3
136 | affinity:
137 | podAntiAffinity:
138 | preferredDuringSchedulingIgnoredDuringExecution:
139 | - weight: 100
140 | podAffinityTerm:
141 | labelSelector:
142 | matchExpressions:
143 | - key: app
144 | operator: In
145 | values:
146 | - elasticsearch
147 | topologyKey: "kubernetes.io/hostname"
148 | domain:
149 | cpu:
150 | cores: 4
151 | sockets: 1
152 | threads: 2
153 | devices:
154 | disks:
155 | - bootOrder: 1
156 | disk:
157 | bus: virtio
158 | name: rootdisk
159 | - disk:
160 | bus: virtio
161 | name: cloudinitdisk
162 | - disk:
163 | bus: virtio
164 | name: elasticsearch-yaml
165 | serial: 00yaml
166 | - disk:
167 | bus: virtio
168 | name: install-scripts
169 | serial: 0000sh
170 | - disk:
171 | bus: virtio
172 | name: ssl-cert-cockpit
173 | serial: 00cert
174 | - disk:
175 | bus: virtio
176 | name: data
177 | serial: datadisk
178 | interfaces:
179 | - masquerade: {}
180 | model: virtio
181 | name: default
182 | resources:
183 | overcommitGuestOverhead: true
184 | requests:
185 | memory: 4Gi
186 | memory:
187 | guest: 8Gi
188 | evictionStrategy: LiveMigrate
189 | networks:
190 | - name: default
191 | pod: {}
192 | terminationGracePeriodSeconds: 180
193 | volumes:
194 | - name: rootdisk
195 | dataVolume:
196 | name: ${VMNAME}
197 | - name: data
198 | dataVolume:
199 | name: ${VMNAME}-data
200 | - name: cloudinitdisk
201 | cloudInitNoCloud:
202 | userData: |-
203 | #cloud-config
204 | hostname: ${VMNAME}
205 | runcmd:
206 | - mount /dev/disk/by-id/virtio-0000sh /mnt/
207 | - bash /mnt/system.sh
208 | - bash /mnt/elasticsearch.sh
209 | - umount /mnt
210 | user: elasticsearch
211 | password: redhat
212 | chpasswd: { expire: False }
213 | ssh_authorized_keys:
214 | - ${SSH_PUBLIC_KEY}
215 | - name: elasticsearch-yaml
216 | configMap:
217 | name: ${VMNAME}-conf
218 | - name: install-scripts
219 | configMap:
220 | name: install-scripts
221 | - name: ssl-cert-cockpit
222 | secret:
223 | # secretName: ssl-cert-cockpit-${VMNAME}
224 | secretName: wildcard-cert
225 | - kind: Route
226 | apiVersion: route.openshift.io/v1
227 | metadata:
228 | name: ${VMNAME}
229 | namespace: ${NAMESPACE}
230 | spec:
231 | host: ${VMNAME}.${BASEDOMAIN}
232 | to:
233 | kind: Service
234 | name: ${VMNAME}
235 | port:
236 | targetPort: cockpit
237 | tls:
238 | termination: passthrough
239 | insecureEdgeTerminationPolicy: Redirect
240 | wildcardPolicy: None
241 | parameters:
242 | - name: VMNAME
243 | description: Virtual Machine Name
244 | required: true
245 | - name: NAMESPACE
246 | description: Namespace to use
247 | required: true
248 | - name: BASEDOMAIN
249 | description: Base Domain for externally exposed services
250 | required: true
251 | - name: SSH_PUBLIC_KEY
252 | description: SSH Public Key
253 | required: true
254 | labels:
255 | demo: ocp-virt
256 |
--------------------------------------------------------------------------------
/windows/.gitignore:
--------------------------------------------------------------------------------
1 | vm.yaml
2 |
--------------------------------------------------------------------------------
/windows/autounattended.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 1
10 | true
11 | Primary
12 |
13 |
14 |
15 |
16 | true
17 | NTFS
18 |
19 | 1
20 | 1
21 |
22 |
23 | 0
24 | true
25 |
26 |
27 |
28 |
29 |
30 |
31 | /IMAGE/NAME
32 | Windows Server 2019 SERVERSTANDARD
33 |
34 |
35 |
36 | 0
37 | 1
38 |
39 |
40 |
41 |
42 | true
43 | Administrator
44 | My Organization
45 |
46 | false
47 |
48 |
49 |
50 | en-US
51 |
52 | en-US
53 | en-US
54 | en-US
55 | en-US
56 |
57 |
58 |
59 |
60 | false
61 |
62 |
63 |
64 |
65 |
66 |
67 | R3dh4t1!
68 | true
69 |
70 | true
71 | 999
72 | Administrator
73 |
74 |
75 | true
76 | true
77 | true
78 | true
79 | Work
80 | 3
81 | true
82 |
83 |
84 |
85 |
86 | Local Administrator Account
87 | Administrator
88 | Administrators
89 | Administrator
90 |
91 |
92 |
93 | Eastern Standard Time
94 |
95 |
96 |
97 |
98 | en-US
99 | en-US
100 | en-US
101 | en-US
102 |
103 |
104 |
105 |
106 | R3dh4t1!
107 | true
108 |
109 | true
110 | 999
111 | Administrator
112 |
113 |
114 | true
115 | true
116 | true
117 | true
118 | Work
119 | 3
120 | true
121 |
122 |
123 |
124 |
125 | Local Administrator Account
126 | Administrator
127 | Administrators
128 | Administrator
129 |
130 |
131 |
132 | Eastern Standard Time
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/windows/vm.yaml.template:
--------------------------------------------------------------------------------
1 | ---
2 | kind: ConfigMap
3 | apiVersion: v1
4 | metadata:
5 | name: windows-vm-sysprep
6 | namespace: {{ namespace }}
7 | data:
8 | Autounattend.xml: >
9 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | 1
22 | true
23 | Primary
24 |
25 |
26 |
27 |
28 | true
29 | NTFS
30 |
31 | 1
32 | 1
33 |
34 |
35 | 0
36 | true
37 |
38 |
39 |
40 |
41 |
42 |
43 | /IMAGE/NAME
44 | Windows Server 2019 SERVERSTANDARD
45 |
46 |
47 |
48 | 0
49 | 1
50 |
51 |
52 |
53 |
54 | true
55 | Administrator
56 | My Organization
57 |
58 | false
59 |
60 |
61 |
62 | en-US
63 |
64 | en-US
65 | en-US
66 | en-US
67 | en-US
68 |
69 |
70 |
71 |
72 | false
73 |
74 |
75 |
76 |
77 |
78 |
79 | R3dh4t1!
80 | true
81 |
82 | true
83 | 999
84 | Administrator
85 |
86 |
87 | true
88 | true
89 | true
90 | true
91 | Work
92 | 3
93 | true
94 |
95 |
96 |
97 |
98 | Local Administrator Account
99 | Administrator
100 | Administrators
101 | Administrator
102 |
103 |
104 |
105 | Eastern Standard Time
106 |
107 |
108 |
109 |
110 | en-US
111 | en-US
112 | en-US
113 | en-US
114 |
115 |
116 |
117 |
118 | R3dh4t1!
119 | true
120 |
121 | true
122 | 999
123 | Administrator
124 |
125 |
126 | true
127 | true
128 | true
129 | true
130 | Work
131 | 3
132 | true
133 |
134 |
135 |
136 |
137 | Local Administrator Account
138 | Administrator
139 | Administrators
140 | Administrator
141 |
142 |
143 |
144 | Eastern Standard Time
145 |
146 |
147 |
148 | ---
149 | apiVersion: kubevirt.io/v1
150 | kind: VirtualMachine
151 | metadata:
152 | name: windows2019
153 | namespace: {{ namespace }}
154 | labels:
155 | app: windows
156 | vm.kubevirt.io/template: windows2k19-server-medium
157 | vm.kubevirt.io/template.namespace: openshift
158 | app.kubernetes.io/part-of: application
159 | annotations:
160 | app.openshift.io/connects-to: '[{"apiVersion":"apps/v1","kind":"Deployment","name":"coordinate"}]'
161 | spec:
162 | dataVolumeTemplates:
163 | - apiVersion: cdi.kubevirt.io/v1beta1
164 | kind: DataVolume
165 | metadata:
166 | annotations:
167 | cdi.kubevirt.io/storage.bind.immediate.requested: 'true'
168 | creationTimestamp: null
169 | name: windows
170 | spec:
171 | source:
172 | blank: {}
173 | storage:
174 | resources:
175 | requests:
176 | storage: 60Gi
177 | - metadata:
178 | creationTimestamp: null
179 | name: windows-installation-cdrom
180 | spec:
181 | source:
182 | pvc:
183 | name: windows-2019
184 | namespace: openshift-virtualization-os-images
185 | storage:
186 | resources:
187 | requests:
188 | storage: 5Gi
189 | running: true
190 | template:
191 | metadata:
192 | annotations:
193 | vm.kubevirt.io/flavor: medium
194 | vm.kubevirt.io/os: windows2k19
195 | vm.kubevirt.io/workload: server
196 | creationTimestamp: null
197 | labels:
198 | kubevirt.io/domain: windows
199 | kubevirt.io/size: medium
200 | spec:
201 | architecture: amd64
202 | domain:
203 | clock:
204 | timer:
205 | hpet:
206 | present: false
207 | hyperv: {}
208 | pit:
209 | tickPolicy: delay
210 | rtc:
211 | tickPolicy: catchup
212 | utc: {}
213 | cpu:
214 | cores: 1
215 | sockets: 1
216 | threads: 1
217 | devices:
218 | disks:
219 | - bootOrder: 2
220 | disk:
221 | bus: sata
222 | name: rootdisk
223 | - bootOrder: 1
224 | cdrom:
225 | bus: sata
226 | name: installation-cdrom
227 | - cdrom:
228 | bus: sata
229 | name: windows-drivers-disk
230 | - cdrom:
231 | bus: sata
232 | name: sysprep
233 | inputs:
234 | - bus: usb
235 | name: tablet
236 | type: tablet
237 | interfaces:
238 | - masquerade: {}
239 | model: e1000e
240 | name: default
241 | memory:
242 | guest: 4Gi
243 | networks:
244 | - name: default
245 | pod: {}
246 | terminationGracePeriodSeconds: 3600
247 | volumes:
248 | - dataVolume:
249 | name: windows
250 | name: rootdisk
251 | - dataVolume:
252 | name: windows-installation-cdrom
253 | name: installation-cdrom
254 | - containerDisk:
255 | image: >-
256 | registry.redhat.io/container-native-virtualization/virtio-win-rhel9
257 | name: windows-drivers-disk
258 | - name: sysprep
259 | sysprep:
260 | configMap:
261 | name: windows-vm-sysprep
262 |
--------------------------------------------------------------------------------
/windows/windows2019.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: cdi.kubevirt.io/v1beta1
3 | kind: DataVolume
4 | metadata:
5 | name: windows-2019
6 | labels:
7 | instancetype.kubevirt.io/default-instancetype: u1.medium
8 | instancetype.kubevirt.io/default-preference: windows
9 | namespace: openshift-virtualization-os-images
10 | spec:
11 | source:
12 | http:
13 | url: 'https://www.opentlc.com/download/ocp4_virt_foundations/Windows2019.iso'
14 | storage:
15 | resources:
16 | requests:
17 | storage: 5Gi
18 |
19 |
--------------------------------------------------------------------------------