{"id":26768042,"url":"https://github.com/rguske/openshift-day-two","last_synced_at":"2025-03-28T21:19:02.803Z","repository":{"id":282387724,"uuid":"925095965","full_name":"rguske/openshift-day-two","owner":"rguske","description":"OpenShift Day-2 Adjustments and Operations","archived":false,"fork":false,"pushed_at":"2025-03-24T09:00:55.000Z","size":881,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-24T10:22:14.158Z","etag":null,"topics":["day2","kubernetes","openshift","openshift-container-platform","redhat"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rguske.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-01-31T08:09:17.000Z","updated_at":"2025-03-24T09:00:58.000Z","dependencies_parsed_at":"2025-03-14T11:24:07.385Z","dependency_job_id":"ffd9b93b-4e40-41ef-939c-5559b8a92881","html_url":"https://github.com/rguske/openshift-day-two","commit_stats":null,"previous_names":["rguske/openshift-day-two"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rguske%2Fopenshift-day-two","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rguske%2Fopenshift-day-two/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rguske%2Fopenshift-day-two/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rguske%2Fopenshift-day-two/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rguske","download_url":"https://codeload.github.com/rguske/openshift-day-two/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246100579,"owners_count":20723479,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["day2","kubernetes","openshift","openshift-container-platform","redhat"],"created_at":"2025-03-28T21:19:02.319Z","updated_at":"2025-03-28T21:19:02.783Z","avatar_url":"https://github.com/rguske.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Red Hat OpenShift Day-2 Operations\n\n⚠️ WIP\n\n- [Red Hat OpenShift Day-2 Operations](#red-hat-openshift-day-2-operations)\n  - [OpenShift Identity Providers](#openshift-identity-providers)\n    - [Configure `htpasswd`](#configure-htpasswd)\n    - [Updating User](#updating-user)\n    - [Configure RBAC Permissions](#configure-rbac-permissions)\n  - [Node Configurations](#node-configurations)\n    - [Make a Control-Plane Node `scheduable`](#make-a-control-plane-node-scheduable)\n  - [Troubleshooting](#troubleshooting)\n    - [Gathering Logs](#gathering-logs)\n  - [Nested Virtualization](#nested-virtualization)\n  - [Replacing the default Ingress Certificate](#replacing-the-default-ingress-certificate)\n  - [OpenShift Web Console Customizations](#openshift-web-console-customizations)\n    - [Customizing the Web Console in OpenShift Container Platform](#customizing-the-web-console-in-openshift-container-platform)\n    - [Customizing the Login/Provider Page](#customizing-the-loginprovider-page)\n  - [Registry Authentication](#registry-authentication)\n  - [Quick NFS Storage](#quick-nfs-storage)\n    - [Install the NFS Server](#install-the-nfs-server)\n    - [OpenShift NFS Provisioner Template](#openshift-nfs-provisioner-template)\n    - [Deploying a Test-workload](#deploying-a-test-workload)\n  - [USB Client Passthrough](#usb-client-passthrough)\n    - [Adjust the VM Configuration (specs)](#adjust-the-vm-configuration-specs)\n    - [Identify USB Vendor and Product ID](#identify-usb-vendor-and-product-id)\n    - [Connect to your VM using `virtctl`](#connect-to-your-vm-using-virtctl)\n    - [Start redirecting the USB Device](#start-redirecting-the-usb-device)\n\n\n## OpenShift Identity Providers\n\n### Configure `htpasswd`\n\n[Docs: Configuring an htpasswd identity provider](https://docs.redhat.com/en/documentation/openshift_container_platform/4.17/html/authentication_and_authorization/configuring-identity-providers#configuring-htpasswd-identity-provider)\n\nStep 1: Create an htpasswd file to store the user and password information:\n\n`htpasswd -c -B -b users.htpasswd rguske \u003cpassword\u003e`\n\nAdd a new user to the file:\n\n`htpasswd -bB users.htpasswd rbohne 'r3dh4t1!'`\n\n`htpasswd -bB users.htpasswd devuser 'r3dh4t1!'`\n\nRemove an existing user:\n\n`htpasswd -D users.htpasswd \u003cusername\u003e`\n\nReplacing an updated `users.htpasswd` file:\n\n`oc create secret generic htpass-secret --from-file=htpasswd=users.htpasswd --dry-run=client -o yaml -n openshift-config | oc replace -f -`\n\nStep 2: Create a Kubernetes secret:\n\n`oc create secret generic htpass-secret-rguske --from-file=htpasswd=\u003cpath_to_rguske.htpasswd\u003e -n openshift-config`\n\n`oc create secret generic htpass-secret-devuser --from-file=htpasswd=\u003cpath_to_devuser.htpasswd\u003e -n openshift-config`\n\nThis can also be done using the OpenShift User Interface:\n\n![configure-oauth-passd](assets/oauth-passwd.png)\n\n### Updating User\n\n`oc get secret htpass-secret -ojsonpath={.data.htpasswd} -n openshift-config | base64 --decode \u003e users.htpasswd`\n\n### Configure RBAC Permissions\n\n[Docs: Using RBAC to define and apply permissions](https://docs.redhat.com/en/documentation/openshift_container_platform/4.17/html/authentication_and_authorization/using-rbac#authorization-overview_using-rbac)\n\n![rbac](assets/rbac.png)\n\nAdd cluster-wide admin priviledges to e.g. user rguske:\n\n```yaml\nkind: ClusterRoleBinding\napiVersion: rbac.authorization.k8s.io/v1\nmetadata:\n  name: rguske-cluster-admin\nsubjects:\n  - kind: User\n    apiGroup: rbac.authorization.k8s.io\n    name: rguske\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: ClusterRole\n  name: cluster-admin\n```\n\nAlternatively via the WebUi:\n\n![rolebinding](assets/rolebinding.png)\n\n## Node Configurations\n\n### Make a Control-Plane Node `scheduable`\n\nRed Hat KB6148012 - [How to schedule pod on master node where scheduling is disabled?](https://access.redhat.com/solutions/6148012)\n\n```code\noc get scheduler cluster -oyaml\n\napiVersion: config.openshift.io/v1\nkind: Scheduler\nmetadata:\n  creationTimestamp: \"2025-01-28T15:20:20Z\"\n  generation: 1\n  name: cluster\n  resourceVersion: \"542\"\n  uid: 59f6fef1-e88a-484a-8e3c-fa38e6e300b3\nspec:\n  mastersSchedulable: false\n  policy:\n    name: \"\"\nstatus: {}\n```\n\nEdit the `scheduler` CR and configure the spec: `mastersSchedulable: true`\n\n```code\noc get nodes\nNAME                  STATUS   ROLES                         AGE     VERSION\nocp1-h5ggj-master-0   Ready    control-plane,master,worker   2d19h   v1.30.6\nocp1-h5ggj-master-1   Ready    control-plane,master,worker   2d19h   v1.30.6\nocp1-h5ggj-master-2   Ready    control-plane,master,worker   2d19h   v1.30.6\nocp1-h5ggj-worker-0   Ready    worker                        2d18h   v1.30.6\nocp1-h5ggj-worker-1   Ready    worker                        2d18h   v1.30.6\n```\n\n## Troubleshooting\n\n### Gathering Logs\n\n[Creating must-gather with more details for specific components in OCP 4\n](https://access.redhat.com/solutions/5459251)\n\nData Collection Audit logs:\n\n`oc adm must-gather -- /usr/bin/gather_audit_logs`\n\nDefault must-gather including the audit logs:\n\n`oc adm must-gather -- '/usr/bin/gather \u0026\u0026 /usr/bin/gather_audit_logs'`\n\nOCPV:\n\n`oc adm must-gather --image-stream=openshift/must-gather --image=registry.redhat.io/container-native-virtualization/cnv-must-gather-rhel[8,9]:[operator_version]`\n\nThe [8,9] should be replaced based on the version of OCP 4.12 uses rhel8, and OCP 4.13 and later uses rhel9.\nThe [operator_version] tag should be in format v4.y.z.\n\nExamples - 4.17: `oc adm must-gather --image-stream=openshift/must-gather --image=registry.redhat.io/container-native-virtualization/cnv-must-gather-rhel8:v4.17.4`\n\n```code\noc adm must-gather \\\n --image-stream=openshift/must-gather \\\n --image=registry.redhat.io/container-native-virtualization/cnv-must-gather-rhel9:v4.17.4 \\\n --image=registry.redhat.io/workload-availability/node-healthcheck-must-gather-rhel9:v0.9.0\n```\n\n[How to generate a sosreport within nodes without SSH in OCP 4](https://access.redhat.com/solutions/4387261)\n\n```code\noc get nodes\nNAME                  STATUS   ROLES                         AGE     VERSION\nocp1-h5ggj-master-0   Ready    control-plane,master,worker   2d19h   v1.30.6\nocp1-h5ggj-master-1   Ready    control-plane,master,worker   2d19h   v1.30.6\nocp1-h5ggj-master-2   Ready    control-plane,master,worker   2d19h   v1.30.6\nocp1-h5ggj-worker-0   Ready    worker                        2d18h   v1.30.6\nocp1-h5ggj-worker-1   Ready    worker                        2d18h   v1.30.6\n```\n\nThen, create a debug session with oc debug node/\u003cnode name\u003e (in this example oc debug node/node-1). The debug session will spawn a pod using the tools image from the release (which doesn't contain sos):\n\n```code\noc debug node/ocp1-h5ggj-master-0\n```\n\n```code\nchroot /host bash\n[root@ocp1-h5ggj-master-0 /]#  cat /etc/redhat-release\nRed Hat Enterprise Linux CoreOS release 4.17\n```\n\n```code\n$ toolbox\n\nTrying to pull registry.redhat.io/rhel9/support-tools:latest...\nGetting image source signatures\nChecking if image destination supports signatures\nCopying blob facf1e7dd3e0 done   |\nCopying blob a0e56de801f5 done   |\nCopying blob ec465ce79861 done   |\nCopying blob cbea42b25984 done   |\nCopying config a627accb68 done   |\nWriting manifest to image destination\nStoring signatures\na627accb682adb407580be0d7d707afbcb90abf2f407a0b0519bacafa15dd409\nSpawning a container 'toolbox-root' with image 'registry.redhat.io/rhel9/support-tools'\nDetected RUN label in the container image. Using that as the default...\nebf4dd2b82bf8ebeab55291c8ca195b61e13c9fc5d8dfb095f5fdcbcdabae2df\ntoolbox-root\nContainer started successfully. To exit, type 'exit'.\n```\n\n`sosreport -e openshift -k crio.all=on -k crio.logs=on  -k podman.all=on -k podman.logs=on --all-logs`\n\n## Nested Virtualization\n\n[How to set the CPU model to Passthrough in OpenShift Virtualization?](https://access.redhat.com/solutions/7069612)\n\n```yaml\noc create -f - \u003c\u003cEOF\napiVersion: kubevirt.io/v1\nkind: VirtualMachine\nmetadata:\n  annotations:\n  labels:\n    app: rhel9-pod-bridge\n    kubevirt.io/dynamic-credentials-support: \"true\"\n  name: rhel9-pod-bridge\nspec:\n  dataVolumeTemplates:\n    - apiVersion: cdi.kubevirt.io/v1beta1\n      kind: DataVolume\n      metadata:\n        name: rhel9-pod-bridge\n      spec:\n        sourceRef:\n          kind: DataSource\n          name: rhel9\n          namespace: openshift-virtualization-os-images\n        storage:\n          accessModes:\n            - ReadWriteMany\n          storageClassName: thin-csi\n          resources:\n            requests:\n              storage: 30Gi\n  running: false\n  template:\n    metadata:\n      annotations:\n        vm.kubevirt.io/flavor: tiny\n        vm.kubevirt.io/os: rhel9\n        vm.kubevirt.io/workload: server\n        kubevirt.io/allow-pod-bridge-network-live-migration: \"\"\n      labels:\n        kubevirt.io/domain: rhel9-pod-bridge\n        kubevirt.io/size: tiny\n    spec:\n      domain:\n        cpu:\n          model: host-passthrough\n          cores: 1\n          sockets: 1\n          threads: 1\n        devices:\n          disks:\n            - disk:\n                bus: virtio\n              name: rootdisk\n            - disk:\n                bus: virtio\n              name: cloudinitdisk\n          interfaces:\n            - bridge: {}\n              name: default\n        machine:\n          type: pc-q35-rhel9.2.0\n        memory:\n          guest: 1.5Gi\n      networks:\n        - name: default\n          pod: {}\n      terminationGracePeriodSeconds: 180\n      volumes:\n        - dataVolume:\n            name: rhel9-pod-bridge\n          name: rootdisk\n        - cloudInitNoCloud:\n            userData: |-\n              #cloud-config\n              user: cloud-user\n              password: redhat\n              chpasswd: { expire: False }\n          name: cloudinitdisk\nEOF\n```\n\nOther sources:\n\n[OpenShift Virtualization reports no nodes are available, cannot start VMs](https://access.redhat.com/solutions/5106121)\n\n[Nested virtualization in OpenShift Virtualization](https://access.redhat.com/solutions/6692341)\n\nEnable features on vSphere:\n\n![nested-v](assets/nested-v-on-vsphere.png)\n\n## Replacing the default Ingress Certificate\n\nPrerequisites:\n\n- You must have a wildcard certificate for the fully qualified .apps subdomain and its corresponding private key. Each should be in a separate PEM format file.\n- The private key must be unencrypted. If your key is encrypted, decrypt it before importing it into OpenShift Container Platform.\n- The certificate must include the subjectAltName extension showing *.apps.\u003cclustername\u003e.\u003cdomain\u003e.\n- The certificate file can contain one or more certificates in a chain. The wildcard certificate must be the first certificate in the file. It can then be followed with any intermediate certificates, and the file should end with the root CA certificate.\n- Copy the root CA certificate into an additional PEM format file.\n- Verify that all certificates which include -----END CERTIFICATE----- also end with one carriage return after that line.\n\nCreate a config map that includes only the root CA certificate used to sign the wildcard certificate:\n\n```yaml\noc apply -f - \u003c\u003cEOF\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: user-ca-bundle\n  namespace: openshift-config\ndata:\n  ca-bundle.crt: |\n    # MyPrivateCA (root.crt)\n    -----BEGIN CERTIFICATE-----\n   zzzzz\n    -----END CERTIFICATE-----\nEOF\n```\n\nUpdate the cluster-wide proxy configuration with the newly created config map:\n\n```code\noc patch proxy/cluster \\\n     --type=merge \\\n     --patch='{\"spec\":{\"trustedCA\":{\"name\":\"user-ca-bundle\"}}}'\n```\n\nCreate a secret that contains the wildcard certificate chain and key:\n\n```code\noc create secret tls ocp1-wildcard-cert \\\n     --cert='/Users/rguske/Downloads/ocp1.rguske/chain.crt' \\\n     --key='/Users/rguske/Downloads/ocp1.rguske/key.key' \\\n     -n openshift-ingress\n```\n\nUpdate the Ingress Controller configuration with the newly created secret:\n\n// Replace the secret name\n\n```code\noc patch ingresscontroller.operator default \\\n     --type=merge -p \\\n     '{\"spec\":{\"defaultCertificate\": {\"name\": \"ocp1-wildcard-cert\"}}}' \\\n     -n openshift-ingress-operator\n```\n\nWatch the ClusterOperator (`co`) for the status update.\n\n## OpenShift Web Console Customizations\n\n### Customizing the Web Console in OpenShift Container Platform\n\n[Docs - Customizing the web console in OpenShift Container Platform](https://docs.openshift.com/container-platform/4.17/web_console/customizing-the-web-console.html)\n\n```code\noc create configmap console-custom-logo --from-file /path/to/console-custom-logo.png -n openshift-config\n```\n\noc create configmap console-custom-logo --from-file '/Users/rguske/Documents/ironman.jpg' -n openshift-config\n\nEdit the web console’s Operator configuration to include customLogoFile and customProductName:\n\n`oc edit consoles.operator.openshift.io cluster`\n\n```yaml\napiVersion: operator.openshift.io/v1\nkind: Console\nmetadata:\n  name: cluster\nspec:\n  customization:\n    customLogoFile:\n      key: ironman.jpg\n      name: console-custom-logo\n    customProductName: My Console\n```\n\nOnce the Operator configuration is updated, it will sync the custom logo config map into the console namespace, mount it to the console pod, and redeploy.\n\nValidate: `oc get clusteroperator console`\n\n### Customizing the Login/Provider Page\n\n[Docs - Customizing the login page](https://docs.openshift.com/container-platform/4.17/web_console/customizing-the-web-console.html)\n\nRun the following commands to create templates you can modify:\n\n`oc adm create-login-template \u003e login.html`\n\nAlternatively, adjust the existing `login.html` and or `provider.html`.\n\nExport the existing `login.html` and `provider.html`:\n\n`POD=$(oc get pods -n openshift-authentication -o name | head -n 1)`\n\n`oc exec -n openshift-authentication \"$POD\" -- cat /var/config/system/secrets/v4-0-config-system-ocp-branding-template/login.html \u003e login.html`\n\n`oc exec -n openshift-authentication \"$POD\" -- cat /var/config/system/secrets/v4-0-config-system-ocp-branding-template/providers.html \u003e providers.html`\n\nChoose an image ewhich you'd like to use for the replacement. Encode the the image into `base64`. [Base64 Guru](https://base64.guru/converter/encode/image) helps.\n\nReplace the base64 value in the `login.html`. Search for `background-image:url(data:image/`, pay attention to the file format (png, svg, jpg), adjust it if necessary and replace the base64 value of the image.\n\nCreate the secrets:\n\n```code\noc -n openshift-config get secret\nNAME                                      TYPE                             DATA   AGE\netcd-client                               kubernetes.io/tls                2      8d\nhtpasswd-dm9mt                            Opaque                           1      6d1h\ninitial-service-account-private-key       Opaque                           1      8d\npull-secret                               kubernetes.io/dockerconfigjson   1      8d\nwebhook-authentication-integrated-oauth   Opaque                           1      8d\n```\n\n`oc create secret generic login-template --from-file=login.html -n openshift-config`\n\n`oc create secret generic providers-template --from-file=providers.html -n openshift-config`\n\nEdit the `oauth` CR:\n\n`oc edit oauths cluster`\n\n```yaml\napiVersion: config.openshift.io/v1\nkind: OAuth\nmetadata:\n  name: cluster\n# ...\nspec:\n  templates:\n    error:\n        name: error-template\n    login:\n        name: login-template\n    providerSelection:\n        name: providers-template\n```\n\nAfter editing the CR, the pods within the `openshift-authentication` namespace will be redeployed.\n\n```code\noc -n openshift-authentication get pods -w\nNAME                              READY   STATUS    RESTARTS   AGE\noauth-openshift-8c7859b9f-fwsnl   1/1     Running   0          6m55s\noauth-openshift-8c7859b9f-kp8rw   1/1     Running   0          7m53s\noauth-openshift-8c7859b9f-qw7wl   1/1     Running   0          7m25s\noauth-openshift-8c7859b9f-kp8rw   1/1     Terminating   0          8m42s\noauth-openshift-664fbb9d49-r5bzk   0/1     Pending       0          0s\noauth-openshift-664fbb9d49-r5bzk   0/1     Pending       0          0s\noauth-openshift-8c7859b9f-kp8rw    0/1     Terminating   0          9m8s\noauth-openshift-664fbb9d49-r5bzk   0/1     Pending       0          26s\noauth-openshift-664fbb9d49-r5bzk   0/1     Pending       0          26s\noauth-openshift-664fbb9d49-r5bzk   0/1     ContainerCreating   0          26s\noauth-openshift-8c7859b9f-kp8rw    0/1     Terminating         0          9m8s\noauth-openshift-8c7859b9f-kp8rw    0/1     Terminating         0          9m8s\noauth-openshift-664fbb9d49-r5bzk   0/1     ContainerCreating   0          27s\noauth-openshift-664fbb9d49-r5bzk   0/1     Running             0          27s\noauth-openshift-664fbb9d49-r5bzk   1/1     Running             0          28s\n```\n\n![provider-image](assets/provider-image.png)\n\n## Registry Authentication\n\n```code\noc create secret docker-registry docker-hub \\\n    --docker-server=docker.io \\\n    --docker-username= \\\n    --docker-password='' \\\n    --docker-email=''\n```\n\noc secrets link default docker-hub --for=pull\n\n## Quick NFS Storage\n\n### Install the NFS Server\n\nIn can be handy to have a NFS backend storage for an OpenShift cluster available quickly. The following instructions guides you through the installation of a NFS server installed on a RHEL bastion host.\n\nInstall the NFS package and activate the service:\n\n```code\ndnf install nfs-utils -y\nsystemctl enable nfs-server.service\nsystemctl start nfs-server.service\nsystemctl status nfs-server.service\n```\n\nCreate the directory in which the Persistent Volumes will be stored in:\n\n```code\nmkdir /srv/nfs-storage-pv-user-pvs\nchmod g+w /srv/nfs-storage-pv-user-pvs\n```\n\nConfigure the folder as well as the network CIDR for the systems which are accessing the NFS server:\n\n```code\nvi /etc/exports\n/srv/nfs-storage-pv-user-pvs  10.198.15.0/24(rw,sync,no_root_squash)\nsystemctl restart nfs-server\nexportfs -arv\nexportfs -s\n```\n\nConfigure the firewall on the RHEL accordingly:\n\n```\nfirewall-cmd --permanent --add-service=nfs\nfirewall-cmd --permanent --add-service=rpc-bind\nfirewall-cmd --permanent --add-service=mountd\nfirewall-cmd --reload\n```\n\n### OpenShift NFS Provisioner Template\n\nWe need a NFS provisioner in order to consume the NFS service. Create the following OpenShift template and make sure to adjust the IP address as well as the path to the NFS folder accordingly at the end of the file:\n\nExample:\n\n```yaml\n- name: NFS_SERVER\n  required: true\n  value: xxx.xxx.xxx.xxx ## IP of the host which runs the NFS server\n- name: NFS_PATH\n  required: true\n  value: /srv/nfs-storage-pv-user-pvs ## folder which was configured on the NFS server\n```\n\nCreate the template:\n\n```yaml\ntee nfs-provisioner-template.yaml \u003e /dev/null \u003c\u003c'EOF'\napiVersion: template.openshift.io/v1\nkind: Template\nlabels:\n  template: nfs-client-provisioner\nmessage: 'NFS storage class ${STORAGE_CLASS} created.'\nmetadata:\n  annotations:\n    description: nfs-client-provisioner\n    openshift.io/display-name: nfs-client-provisioner\n    openshift.io/provider-display-name: Tiger Team\n    tags: infra,nfs\n    template.openshift.io/documentation-url: nfs-client-provisioner\n    template.openshift.io/long-description: nfs-client-provisioner\n    version: 0.0.1\n  name: nfs-client-provisioner\nobjects:\n- kind: Namespace\n  apiVersion: v1\n  metadata:\n    name: ${TARGET_NAMESPACE}\n- kind: ServiceAccount\n  apiVersion: v1\n  metadata:\n    name: nfs-client-provisioner\n    namespace: ${TARGET_NAMESPACE}\n- kind: ClusterRole\n  apiVersion: rbac.authorization.k8s.io/v1\n  metadata:\n    name: nfs-client-provisioner-runner\n  rules:\n    - apiGroups: [\"\"]\n      resources: [\"persistentvolumes\"]\n      verbs: [\"get\", \"list\", \"watch\", \"create\", \"delete\"]\n    - apiGroups: [\"\"]\n      resources: [\"persistentvolumeclaims\"]\n      verbs: [\"get\", \"list\", \"watch\", \"update\"]\n    - apiGroups: [\"storage.k8s.io\"]\n      resources: [\"storageclasses\"]\n      verbs: [\"get\", \"list\", \"watch\"]\n    - apiGroups: [\"\"]\n      resources: [\"events\"]\n      verbs: [\"create\", \"update\", \"patch\"]\n\n- kind: ClusterRoleBinding\n  apiVersion: rbac.authorization.k8s.io/v1\n  metadata:\n    name: run-nfs-client-provisioner\n  subjects:\n    - kind: ServiceAccount\n      name: nfs-client-provisioner\n      namespace: ${TARGET_NAMESPACE}\n  roleRef:\n    kind: ClusterRole\n    name: nfs-client-provisioner-runner\n    apiGroup: rbac.authorization.k8s.io\n\n- kind: Role\n  apiVersion: rbac.authorization.k8s.io/v1\n  metadata:\n    name: nfs-client-provisioner\n    namespace: ${TARGET_NAMESPACE}\n  rules:\n    - apiGroups: [\"\"]\n      resources: [\"endpoints\"]\n      verbs: [\"get\", \"list\", \"watch\", \"create\", \"update\", \"patch\"]\n    - apiGroups: [\"security.openshift.io\"]\n      resourceNames: [\"hostmount-anyuid\"]\n      resources: [\"securitycontextconstraints\"]\n      verbs: [\"use\"]\n\n- kind: RoleBinding\n  apiVersion: rbac.authorization.k8s.io/v1\n  metadata:\n    name: nfs-client-provisioner\n    namespace: ${TARGET_NAMESPACE}\n  subjects:\n    - kind: ServiceAccount\n      name: nfs-client-provisioner\n  roleRef:\n    kind: Role\n    name: nfs-client-provisioner\n    apiGroup: rbac.authorization.k8s.io\n\n- kind: Deployment\n  apiVersion: apps/v1\n  metadata:\n    name: nfs-client-provisioner\n    namespace: ${TARGET_NAMESPACE}\n  spec:\n    replicas: 1\n    selector:\n      matchLabels:\n        app: nfs-client-provisioner\n    strategy:\n      type: Recreate\n    template:\n      metadata:\n        labels:\n          app: nfs-client-provisioner\n      spec:\n        serviceAccountName: nfs-client-provisioner\n        containers:\n          - name: nfs-client-provisioner\n            image: ${PROVISIONER_IMAGE}\n            volumeMounts:\n              - name: nfs-client-root\n                mountPath: /persistentvolumes\n            env:\n              - name: PROVISIONER_NAME\n                value: ${PROVISIONER_NAME}\n              - name: NFS_SERVER\n                value: ${NFS_SERVER}\n              - name: NFS_PATH\n                value: ${NFS_PATH}\n        volumes:\n          - name: nfs-client-root\n            nfs:\n              server: ${NFS_SERVER}\n              path: ${NFS_PATH}\n\n- apiVersion: storage.k8s.io/v1\n  kind: StorageClass\n  metadata:\n    name: managed-nfs-storage\n    annotations:\n      storageclass.kubernetes.io/is-default-class: \"true\"\n  provisioner: ${PROVISIONER_NAME}\n  parameters:\n    archiveOnDelete: \"false\"\n\nparameters:\n- description: Target namespace where nfs-client-provisioner will run.\n  displayName: Target namespace\n  name: TARGET_NAMESPACE\n  required: true\n  value: openshift-nfs-provisioner\n- name: NFS_SERVER\n  required: true\n  value: xxx.xxx.xxx.xxx ## IP of the host which runs the NFS server\n- name: NFS_PATH\n  required: true\n  value: /srv/nfs-storage-pv-user-pvs ## folder which was configured on the NFS server\n- name: PROVISIONER_IMAGE\n  value: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2\n- name: PROVISIONER_NAME\n  value: \"nfs-client-provisioner\"\nEOF\n```\n\nDeploy the template: `oc process -f nfs-provisioner-template.yaml | oc apply -f -`\n\n### Deploying a Test-workload\n\n```yaml\noc -n test1 create -f - \u003c\u003cEOF\nkind: Deployment\napiVersion: apps/v1\nmetadata:\n  name: ubi9\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: ubi9\n  template:\n    metadata:\n      creationTimestamp: null\n      labels:\n        app: ubi9\n    spec:\n      storageClassName: managed-nfs-storage\n      volumes:\n        - name: pvc\n          persistentVolumeClaim:\n            claimName: pvc\n      containers:\n        - name: ubi\n          image: 'registry.access.redhat.com/ubi9/ubi-micro:latest'\n          volumeMounts:\n            - name: pvc\n              mountPath: /pvc\n          command:\n            - /bin/sh\n            - '-c'\n            - |\n              sleep infinity\nEOF\n```\n\nCreate the first `PersistentVolumeClaim` either via the OpenShift Webconsole or via `oc`:\n\n```yaml\noc -n test create -f - \u003c\u003cEOF\nkind: PersistentVolumeClaim\napiVersion: v1\nmetadata:\n  name: pvc\nspec:\n  accessModes:\n    - ReadWriteOnce\n  resources:\n    requests:\n      storage: 5Gi\n  storageClassName: managed-nfs-storage\n  volumeMode: Filesystem\n  accessModes:\n    - ReadWriteOnce\n  capacity:\n    storage: 5Gi\nEOF\n```\n\n## USB Client Passthrough\n\nNot supported in Red Hat OpenShift Virtualization!\n\n- [Kubevirt - Client Passthrough](https://kubevirt.io/user-guide/compute/client_passthrough/#client-passthrough)\n\nFrom the official docs:\n\nSupport for redirection of client's USB device was introduced in release v0.44. This feature is not enabled by default. To enable it, add an empty clientPassthrough under devices, as such:\n\n### Adjust the VM Configuration (specs)\n\n```yaml\nspec:\n  domain:\n    devices:\n      clientPassthrough: {}\n```\n\n\u003e There are two ways of redirecting the same USB devices: Either using its device's vendor and product information or the actual bus and device address information. In Linux, you can gather this info with lsusb, a redacted example below:\n\n### Identify USB Vendor and Product ID\n\nConnect an USB device like e.g. an external CD-Rom device. I've connected it to my MacBook, installed `lsusb` via `brew` and checked for the Vendor ID and Product ID.\n\n```shell\nlsusb\n[...]\nBus 002 Device 001: ID 0e8d:1806 MediaTek Inc. MT1806  Serial: R8RY6GAC60008Y\n[...]\n```\n\n### Connect to your VM using `virtctl`\n\nConnect to your VM running on OpenShift Virtualization.\n\n```shell\nvirtctl console rguske-rhel9\nSuccessfully connected to rguske-rhel9 console. The escape sequence is ^]\n\nrguske-rhel9 login:\n\n[cloud-user@rguske-rhel9 ~]$ lsusb\nBus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub\nBus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub\n```\n\n### Start redirecting the USB Device\n\nOn your local machine, install `virtctl` and the `usbredir`. I've installed both using `brew`.\n\n```shell\nsudo virtctl usbredir 0e8d:1806 rguske-rhel9\n\n{\"component\":\"portforward\",\"level\":\"info\",\"msg\":\"port_arg: '127.0.0.1:49275'\",\"pos\":\"client.go:166\",\"timestamp\":\"2025-03-26T10:19:43.292294Z\"}\n{\"component\":\"portforward\",\"level\":\"info\",\"msg\":\"args: '[--device 0e8d:1806 --to 127.0.0.1:49275]'\",\"pos\":\"client.go:167\",\"timestamp\":\"2025-03-26T10:19:43.293541Z\"}\n{\"component\":\"portforward\",\"level\":\"info\",\"msg\":\"Executing commandline: 'usbredirect [--device 0e8d:1806 --to 127.0.0.1:49275]'\",\"pos\":\"client.go:168\",\"timestamp\":\"2025-03-26T10:19:43.293591Z\"}\n{\"component\":\"portforward\",\"level\":\"info\",\"msg\":\"Connected to usbredirect at 610.549083ms\",\"pos\":\"client.go:132\",\"timestamp\":\"2025-03-26T10:19:43.903058Z\"}\n```\n\nThe output will show the redirection to your Virtual Machine.\n\nOn your target VM, you'll notice:\n\n```shell\n[151999.488527] usb 1-1: new high-speed USB device number 9 using xhci_hcd\n[152000.279607] usb 1-1: New USB device found, idVendor=0e8d, idProduct=1806, bcdDevice= 0.00\n[152000.280126] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3\n[152000.280490] usb 1-1: Product: MT1806\n[152000.280786] usb 1-1: Manufacturer: MediaTek Inc\n[152000.281075] usb 1-1: SerialNumber: R8RY6GAC60008Y\n[152000.548218] usb-storage 1-1:1.0: USB Mass Storage device detected\n[152000.551594] scsi host7: usb-storage 1-1:1.0\n[152001.907628] scsi 7:0:0:0: CD-ROM            ASUS     SDRW-08D3S-U     F201 PQ: 0 ANSI: 0\n[152002.595801] sr 7:0:0:0: [sr0] scsi3-mmc drive: 24x/24x writer dvd-ram cd/rw xa/form2 cdda tray\n[152003.026401] sr 7:0:0:0: Attached scsi generic sg0 type 5\n```\n\nUsing `lsusb` will show the connected device:\n\n```shell\n[cloud-user@rguske-rhel9 ~]$ lsusb\nBus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub\nBus 001 Device 009: ID 0e8d:1806 MediaTek Inc. Samsung SE-208 Slim Portable DVD Writer\nBus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frguske%2Fopenshift-day-two","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frguske%2Fopenshift-day-two","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frguske%2Fopenshift-day-two/lists"}