chore: add local-path-provisioner csi
diff --git a/.github/workflows/csi.yml b/.github/workflows/csi.yml
index a0e43c6..90ff805 100644
--- a/.github/workflows/csi.yml
+++ b/.github/workflows/csi.yml
@@ -43,6 +43,7 @@
       fail-fast: false
       matrix:
         driver:
+          - local-path-provisioner
           - rbd
     steps:
       - name: Checkout project
diff --git a/charts/local-path-provisioner/.helmignore b/charts/local-path-provisioner/.helmignore
new file mode 100644
index 0000000..50af031
--- /dev/null
+++ b/charts/local-path-provisioner/.helmignore
@@ -0,0 +1,22 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/charts/local-path-provisioner/Chart.yaml b/charts/local-path-provisioner/Chart.yaml
new file mode 100644
index 0000000..18c7bd2
--- /dev/null
+++ b/charts/local-path-provisioner/Chart.yaml
@@ -0,0 +1,12 @@
+apiVersion: v1
+description: Use HostPath for persistent local storage with Kubernetes
+name: local-path-provisioner
+version: 0.0.24
+appVersion: "v0.0.24"
+keywords:
+  - storage
+  - hostpath
+kubeVersion: ">=1.12.0-r0"
+home: https://github.com/rancher/local-path-provisioner
+sources:
+  - https://github.com/rancher/local-path-provisioner.git
diff --git a/charts/local-path-provisioner/README.md b/charts/local-path-provisioner/README.md
new file mode 100644
index 0000000..0996394
--- /dev/null
+++ b/charts/local-path-provisioner/README.md
@@ -0,0 +1,116 @@
+# Local Path Provisioner
+
+[Local Path Provisioner](https://github.com/rancher/local-path-provisioner) provides a way for the Kubernetes users to
+utilize the local storage in each node. Based on the user configuration, the Local Path Provisioner will create
+`hostPath` based persistent volume on the node automatically. It utilizes the features introduced by Kubernetes [Local
+Persistent Volume feature](https://kubernetes.io/blog/2018/04/13/local-persistent-volumes-beta/), but make it a simpler
+solution than the built-in `local` volume feature in Kubernetes.
+
+## TL;DR;
+
+```console
+$ git clone https://github.com/rancher/local-path-provisioner.git
+$ cd local-path-provisioner
+$ helm install --name local-path-storage --namespace local-path-storage ./deploy/chart/
+```
+
+## Introduction
+
+This chart bootstraps a [Local Path Provisioner](https://github.com/rancher/local-path-provisioner) deployment on a
+[Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
+
+## Prerequisites
+
+- Kubernetes 1.12+ with Beta APIs enabled
+
+## Installing the Chart
+
+To install the chart with the release name `local-path-storage`:
+
+```console
+$ git clone https://github.com/rancher/local-path-provisioner.git
+$ cd local-path-provisioner
+$ helm install ./deploy/chart/ --name local-path-storage --namespace local-path-storage
+```
+
+The command deploys Local Path Provisioner on the Kubernetes cluster in the default configuration. The
+[configuration](#configuration) section lists the parameters that can be configured during installation.
+
+> **Tip**: List all releases using `helm list`
+
+## Uninstalling the Chart
+
+To uninstall/delete the `local-path-storage` deployment:
+
+```console
+$ helm delete --purge local-path-storage
+```
+
+The command removes all the Kubernetes components associated with the chart and deletes the release.
+
+## Configuration
+
+The following table lists the configurable parameters of the Local Path Provisioner for Kubernetes chart and their
+default values.
+
+| Parameter                           | Description                                                                     | Default                                                                             |
+| ----------------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
+| `image.repository`                  | Local Path Provisioner image name                                               | `rancher/local-path-provisioner`                                                    |
+| `image.tag`                         | Local Path Provisioner image tag                                                | `v0.0.24`                                                                       |
+| `image.pullPolicy`                  | Image pull policy                                                               | `IfNotPresent`                                                                      |
+| `storageClass.create`               | If true, create a `StorageClass`                                                | `true`                                                                              |
+| `storageClass.provisionerName`      | The provisioner name for the storage class                                      | `nil`                                                                               |
+| `storageClass.defaultClass`         | If true, set the created `StorageClass` as the cluster's default `StorageClass` | `false`                                                                             |
+| `storageClass.name`                 | The name to assign the created StorageClass                                     | local-path                                                                          |
+| `storageClass.reclaimPolicy`        | ReclaimPolicy field of the class                                                | Delete                                                                              |
+| `nodePathMap`                       | Configuration of where to store the data on each node                           | `[{node: DEFAULT_PATH_FOR_NON_LISTED_NODES, paths: [/opt/local-path-provisioner]}]` |
+| `resources`                         | Local Path Provisioner resource requests & limits                               | `{}`                                                                                |
+| `rbac.create`                       | If true, create & use RBAC resources                                            | `true`                                                                              |
+| `serviceAccount.create`             | If true, create the Local Path Provisioner service account                      | `true`                                                                              |
+| `serviceAccount.name`               | Name of the Local Path Provisioner service account to use or create             | `nil`                                                                               |
+| `nodeSelector`                      | Node labels for Local Path Provisioner pod assignment                           | `{}`                                                                                |
+| `tolerations`                       | Node taints to tolerate                                                         | `[]`                                                                                |
+| `affinity`                          | Pod affinity                                                                    | `{}`                                                                                |
+| `configmap.setup`                   | Configuration of script to execute setup operations on each node                | #!/bin/sh<br>while getopts "m:s:p:" opt<br>do<br>&emsp;case $opt in <br>&emsp;&emsp;p)<br>&emsp;&emsp;absolutePath=$OPTARG<br>&emsp;&emsp;;;<br>&emsp;&emsp;s)<br>&emsp;&emsp;sizeInBytes=$OPTARG<br>&emsp;&emsp;;;<br>&emsp;&emsp;m)<br>&emsp;&emsp;volMode=$OPTARG<br>&emsp;&emsp;;;<br>&emsp;esac<br>done<br>mkdir -m 0777 -p ${absolutePath}                                    |
+| `configmap.teardown`                | Configuration of script to execute teardown operations on each node             | #!/bin/sh<br>while getopts "m:s:p:" opt<br>do<br>&emsp;case $opt in <br>&emsp;&emsp;p)<br>&emsp;&emsp;absolutePath=$OPTARG<br>&emsp;&emsp;;;<br>&emsp;&emsp;s)<br>&emsp;&emsp;sizeInBytes=$OPTARG<br>&emsp;&emsp;;;<br>&emsp;&emsp;m)<br>&emsp;&emsp;volMode=$OPTARG<br>&emsp;&emsp;;;<br>&emsp;esac<br>done<br>rm -rf ${absolutePath}                                              |
+| `configmap.name`                    | configmap name                                                                  | `local-path-config`                                                                 |
+| `configmap.helperPod`               | helper pod yaml file                                                            | apiVersion: v1<br>kind: Pod<br>metadata:<br>&emsp;name: helper-pod<br>spec:<br>&emsp;containers:<br>&emsp;- name: helper-pod<br>&emsp;&emsp;image: busybox |
+
+
+Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
+
+```console
+$ helm install ./deploy/chart/ --name local-path-storage --namespace local-path-storage --set storageClass.provisionerName=rancher.io/local-path
+```
+
+Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the
+chart. For example,
+
+```console
+$ helm install --name local-path-storage --namespace local-path-storage ./deploy/chart/ -f values.yaml
+```
+
+> **Tip**: You can use the default [values.yaml](values.yaml)
+
+## RBAC
+
+By default the chart will install the recommended RBAC roles and rolebindings.
+
+You need to have the flag `--authorization-mode=RBAC` on the api server. See the following document for how to enable
+[RBAC](https://kubernetes.io/docs/admin/authorization/rbac/).
+
+To determine if your cluster supports RBAC, run the following command:
+
+```console
+$ kubectl api-versions | grep rbac
+```
+
+If the output contains "beta", you may install the chart with RBAC enabled (see below).
+
+### Enable RBAC role/rolebinding creation
+
+To enable the creation of RBAC resources (On clusters with RBAC). Do the following:
+
+```console
+$ helm install ./deploy/chart/ --name local-path-storage --namespace local-path-storage --set rbac.create=true
+```
diff --git a/charts/local-path-provisioner/templates/NOTES.txt b/charts/local-path-provisioner/templates/NOTES.txt
new file mode 100644
index 0000000..25370ac
--- /dev/null
+++ b/charts/local-path-provisioner/templates/NOTES.txt
@@ -0,0 +1,13 @@
+You can create a hostpath-backed persistent volume with a persistent volume claim like this:
+
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+  name: local-path-pvc
+spec:
+  accessModes:
+    - ReadWriteOnce
+  storageClassName: {{ .Values.storageClass.name }}
+  resources:
+    requests:
+      storage: 2Gi
diff --git a/charts/local-path-provisioner/templates/_helpers.tpl b/charts/local-path-provisioner/templates/_helpers.tpl
new file mode 100644
index 0000000..cd58f3d
--- /dev/null
+++ b/charts/local-path-provisioner/templates/_helpers.tpl
@@ -0,0 +1,71 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "local-path-provisioner.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "local-path-provisioner.fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- if contains $name .Release.Name -}}
+{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "local-path-provisioner.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Common labels
+*/}}
+{{- define "local-path-provisioner.labels" -}}
+app.kubernetes.io/name: {{ include "local-path-provisioner.name" . }}
+helm.sh/chart: {{ include "local-path-provisioner.chart" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end -}}
+
+{{/*
+Create the name of the service account to use.
+*/}}
+{{- define "local-path-provisioner.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create -}}
+    {{ default (include "local-path-provisioner.fullname" .) .Values.serviceAccount.name }}
+{{- else -}}
+    {{ default "default" .Values.serviceAccount.name }}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create the name of the provisioner to use.
+*/}}
+{{- define "local-path-provisioner.provisionerName" -}}
+{{- if .Values.storageClass.provisionerName -}}
+{{- printf .Values.storageClass.provisionerName -}}
+{{- else -}}
+cluster.local/{{ template "local-path-provisioner.fullname" . -}}
+{{- end -}}
+{{- end -}}
+
+{{- define "local-path-provisioner.secret" }}
+{{- printf "{\"auths\": {\"%s\": {\"auth\": \"%s\"}}}" .Values.privateRegistry.registryUrl (printf "%s:%s" .Values.privateRegistry.registryUser .Values.privateRegistry.registryPasswd | b64enc) | b64enc }}
+{{- end }}
diff --git a/charts/local-path-provisioner/templates/clusterrole.yaml b/charts/local-path-provisioner/templates/clusterrole.yaml
new file mode 100644
index 0000000..a9fd1c6
--- /dev/null
+++ b/charts/local-path-provisioner/templates/clusterrole.yaml
@@ -0,0 +1,21 @@
+{{- if .Values.rbac.create -}}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: {{ include "local-path-provisioner.fullname" . }}
+  labels:
+{{ include "local-path-provisioner.labels" . | indent 4 }}
+rules:
+- apiGroups: [""]
+  resources: ["nodes", "persistentvolumeclaims", "configmaps"]
+  verbs: ["get", "list", "watch"]
+- apiGroups: [""]
+  resources: ["endpoints", "persistentvolumes", "pods"]
+  verbs: ["*"]
+- apiGroups: [""]
+  resources: ["events"]
+  verbs: ["create", "patch"]
+- apiGroups: ["storage.k8s.io"]
+  resources: ["storageclasses"]
+  verbs: ["get", "list", "watch"]
+{{- end -}}
diff --git a/charts/local-path-provisioner/templates/clusterrolebinding.yaml b/charts/local-path-provisioner/templates/clusterrolebinding.yaml
new file mode 100644
index 0000000..9a46a7b
--- /dev/null
+++ b/charts/local-path-provisioner/templates/clusterrolebinding.yaml
@@ -0,0 +1,16 @@
+{{- if .Values.rbac.create -}}
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: {{ include "local-path-provisioner.fullname" . }}
+  labels:
+{{ include "local-path-provisioner.labels" . | indent 4 }}
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: {{ template "local-path-provisioner.fullname" . }}
+subjects:
+  - kind: ServiceAccount
+    name: {{ template "local-path-provisioner.serviceAccountName" . }}
+    namespace: {{ .Release.Namespace }}
+{{- end -}}
diff --git a/charts/local-path-provisioner/templates/configmap.yaml b/charts/local-path-provisioner/templates/configmap.yaml
new file mode 100644
index 0000000..0d05c2a
--- /dev/null
+++ b/charts/local-path-provisioner/templates/configmap.yaml
@@ -0,0 +1,34 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: {{ .Values.configmap.name }}
+  labels:
+{{ include "local-path-provisioner.labels" . | indent 4 }}
+data:
+  config.json: |-
+    {{- $config := dict }}
+    {{- with .Values.nodePathMap }}
+    {{- $config = set $config "nodePathMap" . }}
+    {{- end }}
+    {{- with .Values.sharedFileSystemPath }}
+    {{- $config = set $config "sharedFileSystemPath" . }}
+    {{- end }}
+    {{- $config | toPrettyJson | nindent 4 }}
+  setup: |-
+    {{ .Values.configmap.setup | nindent 4 }}
+  teardown: |-
+    {{ .Values.configmap.teardown | nindent 4 }}
+  helperPod.yaml: |-
+    apiVersion: v1
+    kind: Pod
+    metadata:
+      name: helper-pod
+    spec:
+      containers:
+        - name: helper-pod
+          {{- if .Values.privateRegistry.registryUrl }}
+          image: {{ .Values.privateRegistry.registryUrl }}/{{ .Values.helperImage.repository }}:{{ .Values.helperImage.tag }}
+          {{- else }}
+          image: {{ .Values.helperImage.repository }}:{{ .Values.helperImage.tag }}
+          {{- end }}
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
diff --git a/charts/local-path-provisioner/templates/deployment.yaml b/charts/local-path-provisioner/templates/deployment.yaml
new file mode 100644
index 0000000..070781e
--- /dev/null
+++ b/charts/local-path-provisioner/templates/deployment.yaml
@@ -0,0 +1,85 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{ include "local-path-provisioner.fullname" . }}
+  labels:
+{{ include "local-path-provisioner.labels" . | indent 4 }}
+spec:
+  replicas: {{ .Values.replicaCount }}
+  selector:
+    matchLabels:
+      app.kubernetes.io/name: {{ include "local-path-provisioner.name" . }}
+      app.kubernetes.io/instance: {{ .Release.Name }}
+  template:
+    metadata:
+      labels:
+        app.kubernetes.io/name: {{ include "local-path-provisioner.name" . }}
+        app.kubernetes.io/instance: {{ .Release.Name }}
+    spec:
+    {{- with .Values.imagePullSecrets }}
+      imagePullSecrets:
+        {{- toYaml . | nindent 8 }}
+    {{- end }}
+      serviceAccountName: {{ template "local-path-provisioner.serviceAccountName" . }}
+      containers:
+        - name: {{ .Chart.Name }}
+        {{- if .Values.privateRegistry.registryUrl }}
+          image: "{{ .Values.privateRegistry.registryUrl }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+        {{- else }}
+          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+        {{- end }}
+          imagePullPolicy: {{ .Values.image.pullPolicy }}
+          command:
+            - local-path-provisioner
+            - --debug
+            - start
+            - --config
+            - /etc/config/config.json
+            - --service-account-name
+            - {{ template "local-path-provisioner.serviceAccountName" . }}
+            - --provisioner-name
+            - {{ template "local-path-provisioner.provisionerName" . }}
+            - --helper-image
+          {{- if .Values.privateRegistry.registryUrl }}
+            - "{{ .Values.privateRegistry.registryUrl }}/{{ .Values.helperImage.repository }}:{{ .Values.helperImage.tag }}"
+          {{- else }}
+            - "{{ .Values.helperImage.repository }}:{{ .Values.helperImage.tag }}"
+          {{- end }}
+            - --configmap-name
+            - {{ .Values.configmap.name }}
+          {{- if .Values.workerThreads }}
+            - --worker-threads
+            - {{ .Values.workerThreads }}
+          {{- end }}
+          {{- if .Values.provisioningRetryCount }}
+            - --provisioning-retry-count
+            - {{ .Values.provisioningRetryCount }}
+          {{- end }}
+          {{- if .Values.deletionRetryCount }}
+            - --deletion-retry-count
+            - {{ .Values.deletionRetryCount }}
+          {{- end }}
+          volumeMounts:
+            - name: config-volume
+              mountPath: /etc/config/
+          env:
+            - name: POD_NAMESPACE
+              value: {{ .Release.Namespace }}
+          resources:
+            {{- toYaml .Values.resources | nindent 12 }}
+      volumes:
+        - name: config-volume
+          configMap:
+            name: {{ .Values.configmap.name }}
+      {{- with .Values.nodeSelector }}
+      nodeSelector:
+        {{- toYaml . | nindent 8 }}
+      {{- end }}
+    {{- with .Values.affinity }}
+      affinity:
+        {{- toYaml . | nindent 8 }}
+    {{- end }}
+    {{- with .Values.tolerations }}
+      tolerations:
+        {{- toYaml . | nindent 8 }}
+    {{- end }}
diff --git a/charts/local-path-provisioner/templates/registry-secret.yaml b/charts/local-path-provisioner/templates/registry-secret.yaml
new file mode 100644
index 0000000..eb33897
--- /dev/null
+++ b/charts/local-path-provisioner/templates/registry-secret.yaml
@@ -0,0 +1,9 @@
+{{- if .Values.defaultSettings.registrySecret }}
+apiVersion: v1
+kind: Secret
+metadata:
+  name: {{ .Values.defaultSettings.registrySecret }}
+type: kubernetes.io/dockerconfigjson
+data:
+  .dockerconfigjson: {{ template "local-path-provisioner.secret" . }}
+{{- end }}
\ No newline at end of file
diff --git a/charts/local-path-provisioner/templates/serviceaccount.yaml b/charts/local-path-provisioner/templates/serviceaccount.yaml
new file mode 100644
index 0000000..9218b84
--- /dev/null
+++ b/charts/local-path-provisioner/templates/serviceaccount.yaml
@@ -0,0 +1,15 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: {{ template "local-path-provisioner.serviceAccountName" . }}
+  labels:
+{{ include "local-path-provisioner.labels" . | indent 4 }}
+imagePullSecrets:
+{{- with .Values.imagePullSecrets }}
+  {{- toYaml . | nindent 2 }}
+{{- end }}
+{{- if .Values.defaultSettings.registrySecret }}
+  - name: {{ .Values.defaultSettings.registrySecret }}
+{{- end }}
+{{- end }}
diff --git a/charts/local-path-provisioner/templates/storageclass.yaml b/charts/local-path-provisioner/templates/storageclass.yaml
new file mode 100644
index 0000000..48f3fd3
--- /dev/null
+++ b/charts/local-path-provisioner/templates/storageclass.yaml
@@ -0,0 +1,16 @@
+{{ if .Values.storageClass.create -}}
+apiVersion: storage.k8s.io/v1
+kind: StorageClass
+metadata:
+  name: {{ .Values.storageClass.name }}
+  labels:
+{{ include "local-path-provisioner.labels" . | indent 4 }}
+{{- if .Values.storageClass.defaultClass }}
+  annotations:
+    storageclass.kubernetes.io/is-default-class: "true"
+{{- end }}
+provisioner: {{ template "local-path-provisioner.provisionerName" . }}
+volumeBindingMode: WaitForFirstConsumer
+reclaimPolicy: {{ .Values.storageClass.reclaimPolicy }}
+allowVolumeExpansion: true
+{{- end }}
diff --git a/charts/local-path-provisioner/values.yaml b/charts/local-path-provisioner/values.yaml
new file mode 100644
index 0000000..b4827cd
--- /dev/null
+++ b/charts/local-path-provisioner/values.yaml
@@ -0,0 +1,118 @@
+# Default values for local-path-provisioner.
+
+replicaCount: 1
+
+image:
+  repository: rancher/local-path-provisioner
+  tag: v0.0.24
+  pullPolicy: IfNotPresent
+
+helperImage:
+  repository: busybox
+  tag: latest
+
+defaultSettings:
+  registrySecret: ~
+
+privateRegistry:
+  registryUrl: ~
+  registryUser: ~
+  registryPasswd: ~
+
+imagePullSecrets: []
+nameOverride: ""
+fullnameOverride: ""
+
+## For creating the StorageClass automatically:
+storageClass:
+  create: true
+
+  ## Set a provisioner name. If unset, a name will be generated.
+  # provisionerName: rancher.io/local-path
+
+  ## Set StorageClass as the default StorageClass
+  ## Ignored if storageClass.create is false
+  defaultClass: false
+
+  ## Set a StorageClass name
+  ## Ignored if storageClass.create is false
+  name: local-path
+
+  ## ReclaimPolicy field of the class, which can be either Delete or Retain
+  reclaimPolicy: Delete
+
+# nodePathMap is the place user can customize where to store the data on each node.
+# 1. If one node is not listed on the nodePathMap, and Kubernetes wants to create volume on it, the paths specified in
+#    DEFAULT_PATH_FOR_NON_LISTED_NODES will be used for provisioning.
+# 2. If one node is listed on the nodePathMap, the specified paths will be used for provisioning.
+#     1. If one node is listed but with paths set to [], the provisioner will refuse to provision on this node.
+#     2. If more than one path was specified, the path would be chosen randomly when provisioning.
+#
+# The configuration must obey following rules:
+# 1. A path must start with /, a.k.a an absolute path.
+# 2. Root directory (/) is prohibited.
+# 3. No duplicate paths allowed for one node.
+# 4. No duplicate node allowed.
+nodePathMap:
+  - node: DEFAULT_PATH_FOR_NON_LISTED_NODES
+    paths:
+      - /opt/local-path-provisioner
+
+# `sharedFileSystemPath` allows the provisioner to use a filesystem that is mounted on all
+# nodes at the same time. In this case all access modes are supported: `ReadWriteOnce`,
+# `ReadOnlyMany` and `ReadWriteMany` for storage claims. In addition
+# `volumeBindingMode: Immediate` can be used in  StorageClass definition.
+# Please note that `nodePathMap` and `sharedFileSystemPath` are mutually exclusive.
+# If `sharedFileSystemPath` is used, then `nodePathMap` must be set to `[]`.
+# sharedFileSystemPath: ""
+
+resources: {}
+  # We usually recommend not to specify default resources and to leave this as a conscious
+  # choice for the user. This also increases chances charts run on environments with little
+  # resources, such as Minikube. If you do want to specify resources, uncomment the following
+  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+  # limits:
+  #   cpu: 100m
+  #   memory: 128Mi
+  # requests:
+  #   cpu: 100m
+  #   memory: 128Mi
+
+rbac:
+  # Specifies whether RBAC resources should be created
+  create: true
+
+serviceAccount:
+  # Specifies whether a ServiceAccount should be created
+  create: true
+  # The name of the ServiceAccount to use.
+  # If not set and create is true, a name is generated using the fullname template
+  name:
+
+nodeSelector: {}
+
+tolerations: []
+
+affinity: {}
+
+configmap:
+  # specify the config map name
+  name: local-path-config
+  # specify the custom script for setup and teardown
+  setup: |-
+    #!/bin/sh
+    set -eu
+    mkdir -m 0777 -p "$VOL_DIR"
+  teardown: |-
+    #!/bin/sh
+    set -eu
+    rm -rf "$VOL_DIR"
+
+# Number of provisioner worker threads to call provision/delete simultaneously.
+# workerThreads: 4
+
+# Number of retries of failed volume provisioning. 0 means retry indefinitely.
+# provisioningRetryCount: 15
+
+# Number of retries of failed volume deletion. 0 means retry indefinitely.
+# deletionRetryCount: 15
diff --git a/hack/sync-charts.sh b/hack/sync-charts.sh
index 09a6a04..527486c 100755
--- a/hack/sync-charts.sh
+++ b/hack/sync-charts.sh
@@ -120,6 +120,10 @@
 curl -sL https://tarballs.opendev.org/openstack/openstack-helm-infra/libvirt-${LIBVIRT_VERSION}.tgz \
   | tar -xz -C ${ATMOSPHERE}/charts
 
+LOCAL_PATH_PROVISIONER_VERSION=0.0.24
+curl -sL https://github.com/rancher/local-path-provisioner/archive/refs/tags/v${LOCAL_PATH_PROVISIONER_VERSION}.tar.gz \
+  | tar -xz -C charts --strip-components=3 ${ATMOSPHERE}/local-path-provisioner-${LOCAL_PATH_PROVISIONER_VERSION}/deploy/chart/
+
 NEUTRON_VERSION=0.3.2
 curl -sL https://tarballs.opendev.org/openstack/openstack-helm/neutron-${NEUTRON_VERSION}.tgz \
   | tar -xz -C ${ATMOSPHERE}/charts
diff --git a/molecule/csi/converge.yml b/molecule/csi/converge.yml
index d97e10c..074266a 100644
--- a/molecule/csi/converge.yml
+++ b/molecule/csi/converge.yml
@@ -13,5 +13,7 @@
 # under the License.
 
 - ansible.builtin.import_playbook: vexxhost.ceph.site
+  when: csi_driver == "rbd"
+
 - ansible.builtin.import_playbook: vexxhost.atmosphere.kubernetes
 - ansible.builtin.import_playbook: vexxhost.atmosphere.csi
diff --git a/roles/csi/meta/main.yml b/roles/csi/meta/main.yml
index 5386ec6..210d14f 100644
--- a/roles/csi/meta/main.yml
+++ b/roles/csi/meta/main.yml
@@ -25,6 +25,8 @@
 
 dependencies:
   - role: defaults
+  - role: local_path_provisioner
+    when: csi_driver == "local-path-provisioner"
   - role: ceph_csi_rbd
     when: csi_driver == "rbd"
   - role: powerstore_csi
diff --git a/roles/defaults/defaults/main.yml b/roles/defaults/defaults/main.yml
index 5806066..e065164 100644
--- a/roles/defaults/defaults/main.yml
+++ b/roles/defaults/defaults/main.yml
@@ -99,6 +99,8 @@
   kube_scheduler: registry.k8s.io/kube-scheduler:v1.22.17
   kube_state_metrics: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.6.0
   libvirt: quay.io/vexxhost/libvirtd:yoga-focal
+  local_path_provisioner_helper: docker.io/library/busybox:1.36.0
+  local_path_provisioner: docker.io/rancher/local-path-provisioner:v0.0.24
   loki: docker.io/grafana/loki:2.7.3
   loki_gateway: docker.io/nginxinc/nginx-unprivileged:1.19-alpine
   magnum_api: quay.io/vexxhost/magnum:zed
diff --git a/roles/local_path_provisioner/README.md b/roles/local_path_provisioner/README.md
new file mode 100644
index 0000000..dce762c
--- /dev/null
+++ b/roles/local_path_provisioner/README.md
@@ -0,0 +1 @@
+# `local_path_provisioner`
diff --git a/roles/local_path_provisioner/defaults/main.yml b/roles/local_path_provisioner/defaults/main.yml
new file mode 100644
index 0000000..c9cbce7
--- /dev/null
+++ b/roles/local_path_provisioner/defaults/main.yml
@@ -0,0 +1,20 @@
+# Copyright (c) 2023 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.
+
+local_path_provisioner_helm_release_name: local-path-provisioner
+local_path_provisioner_helm_chart_path: "../../charts/local-path-provisioner/"
+local_path_provisioner_helm_chart_ref: /usr/local/src/local-path-provisioner
+
+local_path_provisioner_helm_release_namespace: local-path-storage
+local_path_provisioner_helm_values: {}
diff --git a/roles/local_path_provisioner/meta/main.yml b/roles/local_path_provisioner/meta/main.yml
new file mode 100644
index 0000000..a07ef80
--- /dev/null
+++ b/roles/local_path_provisioner/meta/main.yml
@@ -0,0 +1,31 @@
+# Copyright (c) 2022 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 "local-path-provisioner"
+  license: Apache-2.0
+  min_ansible_version: 5.5.0
+  standalone: false
+  platforms:
+    - name: Ubuntu
+      versions:
+        - focal
+
+dependencies:
+  - role: defaults
+  - role: vexxhost.kubernetes.upload_helm_chart
+    vars:
+      upload_helm_chart_src: "{{ local_path_provisioner_helm_chart_path }}"
+      upload_helm_chart_dest: "{{ local_path_provisioner_helm_chart_ref }}"
diff --git a/roles/local_path_provisioner/tasks/main.yml b/roles/local_path_provisioner/tasks/main.yml
new file mode 100644
index 0000000..1554afa
--- /dev/null
+++ b/roles/local_path_provisioner/tasks/main.yml
@@ -0,0 +1,23 @@
+# Copyright (c) 2022 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: Deploy Helm chart
+  run_once: true
+  kubernetes.core.helm:
+    name: "{{ local_path_provisioner_helm_release_name }}"
+    chart_ref: "{{ local_path_provisioner_helm_chart_ref }}"
+    release_namespace: "{{ local_path_provisioner_helm_release_namespace }}"
+    create_namespace: true
+    kubeconfig: /etc/kubernetes/admin.conf
+    values: "{{ _local_path_provisioner_helm_values | combine(local_path_provisioner_helm_values, recursive=True) }}"
diff --git a/roles/local_path_provisioner/vars/main.yml b/roles/local_path_provisioner/vars/main.yml
new file mode 100644
index 0000000..7858845
--- /dev/null
+++ b/roles/local_path_provisioner/vars/main.yml
@@ -0,0 +1,24 @@
+# Copyright (c) 2022 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.
+
+_local_path_provisioner_helm_values:
+  image:
+    repository: "{{ atmosphere_images['local_path_provisioner'] | vexxhost.kubernetes.docker_image('name') }}"
+    tag: "{{ atmosphere_images['local_path_provisioner'] | vexxhost.kubernetes.docker_image('tag') }}"
+  helperImage:
+    repository: "{{ atmosphere_images['local_path_provisioner_helper'] | vexxhost.kubernetes.docker_image('name') }}"
+    tag: "{{ atmosphere_images['local_path_provisioner_helper'] | vexxhost.kubernetes.docker_image('tag') }}"
+  storageClass:
+    defaultClass: true
+    name: general