Add Portworx CSI support (#1167)
diff --git a/doc/source/deploy/csi.rst b/doc/source/deploy/csi.rst
index fc89945..cfd1c56 100644
--- a/doc/source/deploy/csi.rst
+++ b/doc/source/deploy/csi.rst
@@ -6,6 +6,14 @@
your Kubernetes cluster that Atmosphere runs on. You will need to follow the
steps below to enable specific CSI drivers based on your storage requirements.
+.. admonition:: Storing secrets securely
+ :class: tip
+
+ When configuring CSI drivers, it is important to store sensitive
+ information securely. You can use Ansible Vault to encrypt your inventory
+ file and store it in a secure location. For more information on how to
+ use Ansible Vault, refer to the `Ansible documentation <https://docs.ansible.com/ansible/latest/user_guide/vault.html>`_.
+
********
Ceph RBD
********
@@ -41,6 +49,30 @@
infrastructure.
********
+Portworx
+********
+
+If you are using a Pure Storage array for your block storage, you can use the
+Portworx CSI driver to integrate it with your Kubernetes cluster. Portworx
+automatically enables a custom license when integrated with Pure Storage
+arrays (FA/FB edition).
+
+To configure the Portworx CSI driver, update your Ansible inventory as follows:
+
+.. code-block:: yaml
+
+ csi_driver: portworx
+ portworx_pure_flasharray_san_type: <FILL IN> # FC or ISCSI
+ portworx_pure_json:
+ FlaskBlades: []
+ FlashArrays:
+ - MgmtEndPoint: <FILL IN>
+ APIToken: <FILL IN>
+
+For more information about how the ``portworx_pure_json`` variable is used,
+you can refer to the `Pure Storage FlashArray and FlashBlade JSON file reference <https://docs.portworx.com/portworx-enterprise/reference/pure-reference/pure-json-reference>`_.
+
+********
StorPool
********
diff --git a/playbooks/csi.yml b/playbooks/csi.yml
index a0590fe..0b86a74 100644
--- a/playbooks/csi.yml
+++ b/playbooks/csi.yml
@@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-- hosts: controllers[0]
+- hosts: controllers
gather_facts: false
become: true
roles:
diff --git a/roles/csi/meta/main.yml b/roles/csi/meta/main.yml
index 58e6c8b..4615572 100644
--- a/roles/csi/meta/main.yml
+++ b/roles/csi/meta/main.yml
@@ -36,5 +36,7 @@
when: csi_driver == "rbd"
- role: powerstore_csi
when: csi_driver == "powerstore"
+ - role: portworx
+ when: csi_driver == "portworx"
- role: storpool_csi
when: csi_driver == "storpool"
diff --git a/roles/multipathd/handlers/main.yml b/roles/multipathd/handlers/main.yml
index 818a509..e94180b 100644
--- a/roles/multipathd/handlers/main.yml
+++ b/roles/multipathd/handlers/main.yml
@@ -16,3 +16,7 @@
ansible.builtin.service:
name: multipathd
state: reloaded
+
+- name: Reconfigure "multipathd"
+ ansible.builtin.shell:
+ cmd: multipathd -k'reconfigure'
diff --git a/roles/multipathd/tasks/main.yml b/roles/multipathd/tasks/main.yml
index be14d2a..edfff2c 100644
--- a/roles/multipathd/tasks/main.yml
+++ b/roles/multipathd/tasks/main.yml
@@ -12,6 +12,18 @@
# License for the specific language governing permissions and limitations
# under the License.
+- name: Add backports PPA
+ ansible.builtin.apt_repository:
+ repo: ppa:vexxhost/backports
+
+- name: Install the multipathd package
+ ansible.builtin.package:
+ name: multipath-tools
+ state: latest
+ notify:
+ - Reload "multipathd"
+ - Reconfigure "multipathd"
+
- name: Install the configuration file
ansible.builtin.template:
src: multipath.conf.j2
@@ -21,3 +33,4 @@
mode: "0644"
notify:
- Reload "multipathd"
+ - Reconfigure "multipathd"
diff --git a/roles/multipathd/templates/multipath.conf.j2 b/roles/multipathd/templates/multipath.conf.j2
index 6eb0c5f..adc44ac 100644
--- a/roles/multipathd/templates/multipath.conf.j2
+++ b/roles/multipathd/templates/multipath.conf.j2
@@ -2,4 +2,47 @@
defaults {
user_friendly_names {{ multipathd_user_friendly_names | bool | ternary('yes', 'no') }}
+ find_multipaths yes
+}
+
+devices {
+ device {
+ vendor "NVME"
+ product "Pure Storage FlashArray"
+ path_selector "queue-length 0"
+ path_grouping_policy group_by_prio
+ prio ana
+ failback immediate
+ fast_io_fail_tmo 10
+ user_friendly_names no
+ no_path_retry 0
+ features 0
+ dev_loss_tmo 60
+ find_multipaths yes
+ }
+ device {
+ vendor "PURE"
+ product "FlashArray"
+ path_selector "service-time 0"
+ hardware_handler "1 alua"
+ path_grouping_policy group_by_prio
+ prio alua
+ failback immediate
+ path_checker tur
+ fast_io_fail_tmo 10
+ user_friendly_names no
+ no_path_retry 0
+ features 0
+ dev_loss_tmo 600
+ find_multipaths yes
+ }
+}
+
+blacklist {
+ devnode "^pxd[0-9]*"
+ devnode "^pxd*"
+ device {
+ vendor "VMware"
+ product "Virtual disk"
+ }
}
diff --git a/roles/portworx/defaults/main.yml b/roles/portworx/defaults/main.yml
new file mode 100644
index 0000000..27d09bd
--- /dev/null
+++ b/roles/portworx/defaults/main.yml
@@ -0,0 +1,16 @@
+# Copyright (c) 2024 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+# portworx_pure_flasharray_san_type:
+# portworx_pure_json:
diff --git a/roles/portworx/meta/main.yml b/roles/portworx/meta/main.yml
new file mode 100644
index 0000000..e35bb38
--- /dev/null
+++ b/roles/portworx/meta/main.yml
@@ -0,0 +1,33 @@
+# Copyright (c) 2024 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+galaxy_info:
+ author: VEXXHOST, Inc.
+ description: Ansible role for Portworx
+ license: Apache-2.0
+ min_ansible_version: 5.5.0
+ standalone: false
+ platforms:
+ - name: EL
+ versions:
+ - "8"
+ - "9"
+ - name: Ubuntu
+ versions:
+ - focal
+ - jammy
+
+dependencies:
+ - role: defaults
+ - role: multipathd
diff --git a/roles/portworx/tasks/main.yml b/roles/portworx/tasks/main.yml
new file mode 100644
index 0000000..3b3138a
--- /dev/null
+++ b/roles/portworx/tasks/main.yml
@@ -0,0 +1,63 @@
+# Copyright (c) 2024 VEXXHOST, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+- name: Detect if InitiatorName is set
+ ansible.builtin.slurp:
+ src: /etc/iscsi/initiatorname.iscsi
+ register: portworx_iscsi_initiatorname
+
+- name: Configure InitiatorName
+ when: "'InitiatorName' not in portworx_iscsi_initiatorname.content | b64decode"
+ block:
+ - name: Generate a new InitiatorName
+ ansible.builtin.shell:
+ cmd: iscsi-iname
+ register: portworx_iscsi_iname
+
+ - name: Write the new InitiatorName
+ ansible.builtin.copy:
+ content: "InitiatorName={{ portworx_iscsi_iname.stdout }}"
+ dest: /etc/iscsi/initiatorname.iscsi
+ owner: root
+ group: root
+ mode: "0644"
+
+- name: Install Portworx
+ run_once: true
+ kubernetes.core.k8s:
+ state: present
+ template:
+ - portworx.yml
+ - config.yml
+
+- name: Wait till the CRDs are created
+ run_once: true
+ kubernetes.core.k8s_info:
+ api_version: apiextensions.k8s.io/v1
+ kind: CustomResourceDefinition
+ name: storageclusters.core.libopenstorage.org
+ # NOTE(mnaser): Portworx operator creates the CRDs for the cluster
+ # so we need to make sure they're created before we proceed.
+ retries: 60
+ delay: 5
+ register: _result
+ until: _result.resources | length > 0
+
+- name: Create Portworx Storage Cluster
+ run_once: true
+ kubernetes.core.k8s:
+ state: present
+ template:
+ - storage_cluster.yml
+ - storage_class.yml
diff --git a/roles/portworx/templates/config.yml b/roles/portworx/templates/config.yml
new file mode 100644
index 0000000..5f8f604
--- /dev/null
+++ b/roles/portworx/templates/config.yml
@@ -0,0 +1,8 @@
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: px-pure-secret
+ namespace: portworx
+stringData:
+ pure.json: '{{ portworx_pure_json | to_json }}'
diff --git a/roles/portworx/templates/portworx.yml b/roles/portworx/templates/portworx.yml
new file mode 100644
index 0000000..65bb35c
--- /dev/null
+++ b/roles/portworx/templates/portworx.yml
@@ -0,0 +1,80 @@
+# SOURCE: https://install.portworx.com/?comp=pxoperator
+
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: portworx-operator
+ namespace: kube-system
+---
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: portworx-operator
+rules:
+ - apiGroups: ["*"]
+ resources: ["*"]
+ verbs: ["*"]
+---
+kind: ClusterRoleBinding
+apiVersion: rbac.authorization.k8s.io/v1
+metadata:
+ name: portworx-operator
+subjects:
+- kind: ServiceAccount
+ name: portworx-operator
+ namespace: kube-system
+roleRef:
+ kind: ClusterRole
+ name: portworx-operator
+ apiGroup: rbac.authorization.k8s.io
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: portworx-operator
+ namespace: kube-system
+spec:
+ strategy:
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 1
+ type: RollingUpdate
+ replicas: 1
+ selector:
+ matchLabels:
+ name: portworx-operator
+ template:
+ metadata:
+ labels:
+ name: portworx-operator
+ spec:
+ containers:
+ - name: portworx-operator
+ imagePullPolicy: Always
+ image: portworx/px-operator:23.10.5
+ command:
+ - /operator
+ - --verbose
+ - --driver=portworx
+ - --leader-elect=true
+ env:
+ - name: OPERATOR_NAME
+ value: portworx-operator
+ - name: POD_NAME
+ valueFrom:
+ fieldRef:
+ fieldPath: metadata.name
+ affinity:
+ podAntiAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ - labelSelector:
+ matchExpressions:
+ - key: "name"
+ operator: In
+ values:
+ - portworx-operator
+ topologyKey: "kubernetes.io/hostname"
+ serviceAccountName: portworx-operator
+ # NOTE(mnaser): Add this to keep running on control plane
+ nodeSelector:
+ openstack-control-plane: enabled
diff --git a/roles/portworx/templates/storage_class.yml b/roles/portworx/templates/storage_class.yml
new file mode 100644
index 0000000..c935e24
--- /dev/null
+++ b/roles/portworx/templates/storage_class.yml
@@ -0,0 +1,10 @@
+kind: StorageClass
+apiVersion: storage.k8s.io/v1
+metadata:
+ name: general
+ annotations:
+ storageclass.kubernetes.io/is-default-class: "true"
+provisioner: pxd.portworx.com
+parameters:
+ backend: pure_block
+allowVolumeExpansion: true
diff --git a/roles/portworx/templates/storage_cluster.yml b/roles/portworx/templates/storage_cluster.yml
new file mode 100644
index 0000000..ae3f5a5
--- /dev/null
+++ b/roles/portworx/templates/storage_cluster.yml
@@ -0,0 +1,53 @@
+---
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: portworx
+---
+kind: StorageCluster
+apiVersion: core.libopenstorage.org/v1
+metadata:
+ name: px-cluster-567cacc6-e39c-49da-9d35-2bafacfcf18c
+ namespace: portworx
+ annotations:
+ portworx.io/install-source: "https://install.portworx.com/?operator=true&mc=false&kbver=1.28.0&ns=portworx&oem=esse&user=2487eaa2-e557-4f68-8e87-9b56c0a4498f&b=true&iop=6&s=%22size%3D150%22&pureSanType=FC&ce=pure&c=px-cluster-567cacc6-e39c-49da-9d35-2bafacfcf18c&stork=true&csi=true&mon=true&tel=true&st=k8s&promop=true"
+ portworx.io/misc-args: "--oem esse"
+ portworx.io/disable-storage-class: "true"
+ portworx.io/pvc-controller-secure-port: "20257"
+spec:
+ image: portworx/oci-monitor:3.1.1
+ imagePullPolicy: Always
+ kvdb:
+ internal: true
+ cloudStorage:
+ deviceSpecs:
+ - size=150
+ secretsProvider: k8s
+ stork:
+ enabled: true
+ args:
+ webhook-controller: "true"
+ autopilot:
+ enabled: true
+ runtimeOptions:
+ default-io-profile: "6"
+ csi:
+ enabled: true
+ monitoring:
+ telemetry:
+ enabled: true
+ prometheus:
+ enabled: false
+ exportMetrics: true
+ env:
+ - name: PURE_FLASHARRAY_SAN_TYPE
+ value: "{{ portworx_pure_flasharray_san_type }}"
+ placement:
+ nodeAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ nodeSelectorTerms:
+ - matchExpressions:
+ - key: openstack-control-plane
+ operator: In
+ values:
+ - "enabled"
diff --git a/roles/powerstore_csi/tasks/main.yml b/roles/powerstore_csi/tasks/main.yml
index ec85eb5..5caec12 100644
--- a/roles/powerstore_csi/tasks/main.yml
+++ b/roles/powerstore_csi/tasks/main.yml
@@ -1,11 +1,13 @@
---
- name: Clone PowerStore CSI from GitHub
+ run_once: true
ansible.builtin.git:
repo: https://github.com/dell/csi-powerstore.git
dest: /var/lib/csi-powerstore
version: v2.3.0
- name: Create Secret
+ run_once: true
kubernetes.core.k8s:
state: present
definition:
@@ -18,6 +20,7 @@
config: "{{ powerstore_csi_config | to_yaml }}"
- name: Create StorageClass
+ run_once: true
kubernetes.core.k8s:
state: present
definition:
@@ -33,6 +36,7 @@
volumeBindingMode: Immediate
- name: Deploy Helm chart
+ run_once: true
kubernetes.core.helm:
name: csi-powerstore
chart_ref: /var/lib/csi-powerstore/helm/csi-powerstore