Skip to content

Commit

Permalink
ONTAP NAS Qtree driver
Browse files Browse the repository at this point in the history
Added support for the ontap-nas-economy driver to Trident.

Fixes #2
Fixes #40
  • Loading branch information
clintonk committed Sep 19, 2017
1 parent 9f72c5d commit 7df1731
Show file tree
Hide file tree
Showing 12 changed files with 184 additions and 53 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
**Fixes:**
- tridentctl correctly handles larger payloads using chunked encoding.
- Trident installs correctly in a Kubernetes pod with E-series and ONTAP SAN.
- Trident allows periods in PVC names.

**Enhancements:**
- Controller serial numbers are reported by the REST interface and tridentctl.
- tridentctl logs can display launcher and ephemeral logs, and it can create a
support archive.
- Added ontap-nas-economy driver.

## v17.07.0

Expand Down
37 changes: 24 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ Kubernetes.
This will be also used in step 8 to provision the volume on which Trident
will store its metadata.

Edit either `sample-input/backend-ontap-nas.json`, `sample-input/backend-ontap-san.json`,
`sample-input/backend-solidfire.json`, or `sample-input/sample-eseries-iscsi.json`
to specify an ONTAP NAS, ONTAP SAN, SolidFire, or E-Series backend.
Edit either `sample-input/backend-ontap-nas.json`, `sample-input/backend-ontap-nas-economy.json`,
`sample-input/backend-ontap-san.json`, `sample-input/backend-solidfire.json`, or
`sample-input/sample-eseries-iscsi.json` to specify an ONTAP NAS, ONTAP SAN, SolidFire, or E-Series backend.

If multiple clusters will be running Trident against the same backend, add
the `storagePrefix` attribute, with a value that will be unique to your
Expand Down Expand Up @@ -166,7 +166,7 @@ Kubernetes.
Edit the `backendType` parameter of
`sample-input/storage-class-basic.yaml.templ` and replace
`__BACKEND_TYPE__` with either `ontap-nas`, `ontap-san`, `solidfire-san`,
`__BACKEND_TYPE__` with either `ontap-nas`, `ontap-nas-economy`, `ontap-san`, `solidfire-san`,
or `eseries-iscsi` depending on the backend created in the previous steps.
Save it as `sample-input/storage-class-basic.yaml`.
Expand Down Expand Up @@ -592,7 +592,7 @@ must be created for NAS and SAN.
| Attribute | Type | Required | Description |
| --------- | ---- | --- | ----------- |
| version | int | No | Version of the nDVP API in use. |
| storageDriverName | string | Yes | Must be either "ontap-nas" or "ontap-san". |
| storageDriverName | string | Yes | Must be one of "ontap-nas", "ontap-nas-economy", or "ontap-san". |
| storagePrefix | string | No | Prefix to prepend to volumes created on the backend. The format of the resultant volume name will be `<prefix>_<volumeName>`; this prefix should be chosen so that volume names are unique. If unspecified, this defaults to `trident`.|
| managementLIF | string | Yes | IP address of the cluster or SVM management LIF. |
| dataLIF | string | Yes | IP address of the SVM data LIF to use for connecting to provisioned volumes. |
Expand All @@ -615,9 +615,20 @@ may mount Trident volumes (e.g., all nodes in the Kubernetes cluster that would
attach volumes) must be mapped into this iGroup. This must be configured
before these hosts can mount and attach Trident volumes from this backend.
The ontap-nas and ontap-san backend types create an ONTAP FlexVol for each persistent volume. ONTAP supports up to 1000
FlexVols per cluster node with a cluster maximum of 12,000 FlexVols. If your Kubernetes volume requirements fit within
that limitation, the ontap-nas driver is the preferred NAS solution due to the additional features offered by FlexVols
such as PV-granular snapshots. If you need more persistent volumes than may be accommodated by the FlexVol limits,
choose the ontap-nas-economy driver, which creates volumes as ONTAP Qtrees within a pool of automatically managed
FlexVols. Qtrees offer far greater scaling, up to 100,000 per cluster node and 2,400,000 per cluster, at the expense of
some features such as PV-granular snapshots. To get advanced features and huge scale in the same environment, you can
configure multiple backends, with some using ontap-nas and others using ontap-nas-economy.
`sample-input/backend-ontap-nas.json` provides an example of an ONTAP NAS
backend configuration. `sample-input/backend-ontap-san.json` and
`sample-input/backend-ontap-san-full.json` provide the same for an ONTAP SAN
backend configuration. `sample-input/backend-ontap-nas-economy.json` provides
an example of an ONTAP NAS backend configuration that can support far more volumes,
albeit without certain features such as snapshots. `sample-input/backend-ontap-san.json`
and `sample-input/backend-ontap-san-full.json` provide examples for an ONTAP SAN
backend; the latter includes all available configuration options.
##### SolidFire Configurations
Expand Down Expand Up @@ -773,7 +784,7 @@ The current attributes and their possible values are below:
| --------- | ---- | ------ | ----------- | --- |
| media | string | hdd, hybrid, ssd | Type of media used by the storage pool. Hybrid indicates both HDD and SSD. | Type of media desired for the volume. |
| provisioningType | string | thin, thick | Types of provisioning supported by the storage pool. | Whether volumes will be created with thick or thin provisioning. |
| backendType | string | ontap-nas, ontap-san, solidfire-san, eseries-iscsi | Backend to which the storage pool belongs. | Specific type of backend on which to provision volumes. |
| backendType | string | ontap-nas, ontap-nas-economy, ontap-san, solidfire-san, eseries-iscsi | Backend to which the storage pool belongs. | Specific type of backend on which to provision volumes. |
| snapshots | bool | true, false | Whether the backend supports snapshots. | Whether volumes must have snapshot support. |
| IOPS | int | positive integers | IOPS range the storage pool is capable of providing. | Target IOPS for the volume to be created. |
Expand Down Expand Up @@ -1029,11 +1040,11 @@ corresponding PV, Trident follows the following rules:
| Annotation | Volume Parameter | Supported Drivers |
| ---------- | ---------------- | ----------------- |
| `trident.netapp.io/protocol` | `protocol` | `ontap-nas`, `ontap-san`, `solidfire-san` |
| `trident.netapp.io/exportPolicy` | `exportPolicy`| `ontap-nas`, `ontap-san` |
| `trident.netapp.io/snapshotPolicy` | `snapshotPolicy`| `ontap-nas`, `ontap-san` |
| `trident.netapp.io/snapshotDirectory` | `snapshotDirectory`| `ontap-nas` |
| `trident.netapp.io/unixPermissions` | `unixPermissions`| `ontap-nas` |
| `trident.netapp.io/protocol` | `protocol` | `ontap-nas`, `ontap-nas-economy`, `ontap-san`, `solidfire-san` |
| `trident.netapp.io/exportPolicy` | `exportPolicy`| `ontap-nas`, `ontap-nas-economy`, `ontap-san` |
| `trident.netapp.io/snapshotPolicy` | `snapshotPolicy`| `ontap-nas`, `ontap-nas-economy`, `ontap-san` |
| `trident.netapp.io/snapshotDirectory` | `snapshotDirectory`| `ontap-nas`, `ontap-nas-economy` |
| `trident.netapp.io/unixPermissions` | `unixPermissions`| `ontap-nas`, `ontap-nas-economy` |
| `trident.netapp.io/blockSize` | `blockSize`| `solidfire-san` |
| `trident.netapp.io/fileSystem` | `fileSystem` | `ontap-san`, `solidfire-san`, `eseries-iscsi` |
| `trident.netapp.io/reclaimPolicy` | N/A | N/A |
Expand Down
3 changes: 2 additions & 1 deletion frontend/kubernetes/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,8 @@ func (p *KubernetesPlugin) createVolumeAndPV(uniqueName string,
driverType == dvp.EseriesIscsiStorageDriverName:
iscsiSource = CreateISCSIVolumeSource(vol.Config)
pv.Spec.ISCSI = iscsiSource
case driverType == dvp.OntapNASStorageDriverName:
case driverType == dvp.OntapNASStorageDriverName ||
driverType == dvp.OntapNASQtreeStorageDriverName:
nfsSource = CreateNFSVolumeSource(vol.Config)
pv.Spec.NFS = nfsSource
case driverType == fake.FakeStorageDriverName:
Expand Down
6 changes: 3 additions & 3 deletions glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion glide.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ import:
- jlexer
- jwriter
- package: github.com/netapp/netappdvp
version: 80bb4de1bcbbecbfa787c962d7dc9c130ad5d801
version: c1ffa79d6458cad84773affcd3d1cc0137691e1a
subpackages:
- apis/eseries
- apis/ontap
Expand Down
7 changes: 7 additions & 0 deletions storage/factory/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ func NewStorageBackendForConfig(configJSON string) (
switch commonConfig.StorageDriverName {
case dvp.OntapNASStorageDriverName:
storageDriver = &ontap.OntapNASStorageDriver{}
case dvp.OntapNASQtreeStorageDriverName:
storageDriver = &ontap.OntapNASQtreeStorageDriver{}
case dvp.OntapSANStorageDriverName:
storageDriver = &ontap.OntapSANStorageDriver{}
case dvp.SolidfireSANStorageDriverName:
Expand Down Expand Up @@ -83,6 +85,11 @@ func NewStorageBackendForConfig(configJSON string) (
// Post-driver initialization setup
switch commonConfig.StorageDriverName {
case dvp.OntapNASStorageDriverName:
break

case dvp.OntapNASQtreeStorageDriverName:
break

case dvp.OntapSANStorageDriverName:
driver := storageDriver.(*ontap.OntapSANStorageDriver)
if driver.Config.IgroupName == "netappdvp" {
Expand Down
39 changes: 17 additions & 22 deletions storage/ontap/ontap_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,16 @@ const (
)

var ontapPerformanceClasses = map[ontapPerformanceClass]map[string]sa.Offer{
ontapHDD: map[string]sa.Offer{
sa.Media: sa.NewStringOffer(sa.HDD),
},
ontapHybrid: map[string]sa.Offer{
sa.Media: sa.NewStringOffer(sa.Hybrid),
},
ontapSSD: map[string]sa.Offer{
sa.Media: sa.NewStringOffer(sa.SSD),
},
}

func getCommonONTAPStoragePoolAttributes(pool *storage.StoragePool) {
// ONTAP supports snapshots
pool.Attributes[sa.Snapshots] = sa.NewBoolOffer(true)
// ONTAP volumes support both thick and thin provisioning.
pool.Attributes[sa.ProvisioningType] = sa.NewStringOffer("thick", "thin")
ontapHDD: {sa.Media: sa.NewStringOffer(sa.HDD)},
ontapHybrid: {sa.Media: sa.NewStringOffer(sa.Hybrid)},
ontapSSD: {sa.Media: sa.NewStringOffer(sa.SSD)},
}

// getStorageBackendSpecsCommon discovers the aggregates assigned to the configured SVM, and it updates the specified StorageBackend
// object with StoragePools and their associated attributes.
func getStorageBackendSpecsCommon(d dvp.OntapStorageDriver, backend *storage.StorageBackend) (err error) {
func getStorageBackendSpecsCommon(
d dvp.OntapStorageDriver, backend *storage.StorageBackend, poolAttributes map[string]sa.Offer,
) (err error) {

api := d.GetAPI()
config := d.GetConfig()
Expand Down Expand Up @@ -104,10 +93,13 @@ func getStorageBackendSpecsCommon(d dvp.OntapStorageDriver, backend *storage.Sto
"such as 'media' will not match pools on this backend. %v", err)
}

// Add common attributes and register pools with backend
// Add attributes common to each pool and register pools with backend
for _, pool := range storagePools {
pool.Attributes[sa.BackendType] = sa.NewStringOffer(driverName)
getCommonONTAPStoragePoolAttributes(pool)

for attrName, offer := range poolAttributes {
pool.Attributes[attrName] = offer
}

backend.AddStoragePool(pool)
}

Expand Down Expand Up @@ -255,8 +247,11 @@ func getVolumeOptsCommon(
return opts
}

func getInternalVolumeNameCommon(name string) string {
return strings.Replace(name, "-", "_", -1)
func getInternalVolumeNameCommon(config *dvp.CommonStorageDriverConfig, name string) string {
s1 := storage.GetCommonInternalVolumeName(config, name)
s2 := strings.Replace(s1, "-", "_", -1)
s3 := strings.Replace(s2, ".", "_", -1)
return s3
}

/*func createPrepareCommon(volConfig *storage.VolumeConfig) bool {
Expand Down
17 changes: 12 additions & 5 deletions storage/ontap/ontap_nas.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,17 @@ type OntapNASStorageDriver struct {
func (d *OntapNASStorageDriver) GetStorageBackendSpecs(backend *storage.StorageBackend) error {

backend.Name = "ontapnas_" + d.Config.DataLIF
return getStorageBackendSpecsCommon(d, backend)
poolAttrs := d.GetStoragePoolAttributes()
return getStorageBackendSpecsCommon(d, backend, poolAttrs)
}

func (d *OntapNASStorageDriver) GetStoragePoolAttributes() map[string]sa.Offer {

return map[string]sa.Offer{
sa.BackendType: sa.NewStringOffer(d.Name()),
sa.Snapshots: sa.NewBoolOffer(true),
sa.ProvisioningType: sa.NewStringOffer("thick", "thin"),
}
}

func (d *OntapNASStorageDriver) GetVolumeOpts(
Expand All @@ -31,10 +41,7 @@ func (d *OntapNASStorageDriver) GetVolumeOpts(
}

func (d *OntapNASStorageDriver) GetInternalVolumeName(name string) string {
return getInternalVolumeNameCommon(
storage.GetCommonInternalVolumeName(d.Config.CommonStorageDriverConfig,
name),
)
return getInternalVolumeNameCommon(d.Config.CommonStorageDriverConfig, name)
}

func (d *OntapNASStorageDriver) CreatePrepare(
Expand Down
91 changes: 91 additions & 0 deletions storage/ontap/ontap_nas_qtree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 2016 NetApp, Inc. All Rights Reserved.

package ontap

import (
"fmt"

dvp "github.com/netapp/netappdvp/storage_drivers"
"github.com/netapp/trident/config"
"github.com/netapp/trident/storage"
sa "github.com/netapp/trident/storage_attribute"
)

// OntapNASQtreeStorageDriver is for NFS storage provisioning
type OntapNASQtreeStorageDriver struct {
dvp.OntapNASQtreeStorageDriver
}

// Retrieve storage backend capabilities
func (d *OntapNASQtreeStorageDriver) GetStorageBackendSpecs(backend *storage.StorageBackend) error {

backend.Name = "ontapnaseco_" + d.Config.DataLIF
poolAttrs := d.GetStoragePoolAttributes()
return getStorageBackendSpecsCommon(d, backend, poolAttrs)
}

func (d *OntapNASQtreeStorageDriver) GetStoragePoolAttributes() map[string]sa.Offer {

return map[string]sa.Offer{
sa.BackendType: sa.NewStringOffer(d.Name()),
sa.Snapshots: sa.NewBoolOffer(false),
sa.ProvisioningType: sa.NewStringOffer("thick", "thin"),
}
}

func (d *OntapNASQtreeStorageDriver) GetVolumeOpts(
volConfig *storage.VolumeConfig,
vc *storage.StoragePool,
requests map[string]sa.Request,
) (map[string]string, error) {
return getVolumeOptsCommon(volConfig, vc, requests), nil
}

func (d *OntapNASQtreeStorageDriver) GetInternalVolumeName(name string) string {
return getInternalVolumeNameCommon(d.Config.CommonStorageDriverConfig, name)
}

func (d *OntapNASQtreeStorageDriver) CreatePrepare(volConfig *storage.VolumeConfig) bool {
// Sanitize the volume name
volConfig.InternalName = d.GetInternalVolumeName(volConfig.Name)

// Because the storage prefix specified in the backend config must create
// a unique set of volume names, we do not need to check whether volumes
// exist in the backend here.
return true
}

func (d *OntapNASQtreeStorageDriver) CreateFollowup(volConfig *storage.VolumeConfig) error {

// Determine which Flexvol contains the qtree
exists, flexvol, err := d.API.QtreeExists(volConfig.InternalName, d.OntapNASQtreeStorageDriver.FlexvolNamePrefix())
if err != nil {
return fmt.Errorf("Could not determine if qtree %s exists. %v", volConfig.InternalName, err)
}
if !exists {
return fmt.Errorf("Could not find qtree %s", volConfig.InternalName)
}

// Set export path info on the volume config
volConfig.AccessInfo.NfsServerIP = d.Config.DataLIF
volConfig.AccessInfo.NfsPath = fmt.Sprintf("/%s/%s", flexvol, volConfig.InternalName)

return nil
}

func (d *OntapNASQtreeStorageDriver) GetProtocol() config.Protocol {
return config.File
}

func (d *OntapNASQtreeStorageDriver) GetDriverName() string {
return d.Config.StorageDriverName
}

func (d *OntapNASQtreeStorageDriver) StoreConfig(b *storage.PersistentStorageBackendConfig) {
storage.SanitizeCommonStorageDriverConfig(d.Config.CommonStorageDriverConfig)
b.OntapConfig = &d.Config
}

func (d *OntapNASQtreeStorageDriver) GetExternalConfig() interface{} {
return getExternalConfig(d.Config)
}
17 changes: 12 additions & 5 deletions storage/ontap/ontap_san.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@ type OntapSANStorageDriver struct {
func (d *OntapSANStorageDriver) GetStorageBackendSpecs(backend *storage.StorageBackend) error {

backend.Name = "ontapsan_" + d.Config.DataLIF
return getStorageBackendSpecsCommon(d, backend)
poolAttrs := d.GetStoragePoolAttributes()
return getStorageBackendSpecsCommon(d, backend, poolAttrs)
}

func (d *OntapSANStorageDriver) GetStoragePoolAttributes() map[string]sa.Offer {

return map[string]sa.Offer{
sa.BackendType: sa.NewStringOffer(d.Name()),
sa.Snapshots: sa.NewBoolOffer(true),
sa.ProvisioningType: sa.NewStringOffer("thick", "thin"),
}
}

func (d *OntapSANStorageDriver) GetVolumeOpts(
Expand All @@ -35,10 +45,7 @@ func (d *OntapSANStorageDriver) GetVolumeOpts(
}

func (d *OntapSANStorageDriver) GetInternalVolumeName(name string) string {
return getInternalVolumeNameCommon(
storage.GetCommonInternalVolumeName(d.Config.CommonStorageDriverConfig,
name),
)
return getInternalVolumeNameCommon(d.Config.CommonStorageDriverConfig, name)
}

func (d *OntapSANStorageDriver) CreatePrepare(
Expand Down
7 changes: 4 additions & 3 deletions storage/solidfire/solidfire_san.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@ func (d *SolidfireSANStorageDriver) GetStorageBackendSpecs(
}

func (d *SolidfireSANStorageDriver) GetInternalVolumeName(name string) string {
internalName := storage.GetCommonInternalVolumeName(
d.Config.CommonStorageDriverConfig, name)
return strings.Replace(internalName, "_", "-", -1)
s1 := storage.GetCommonInternalVolumeName(d.Config.CommonStorageDriverConfig, name)
s2 := strings.Replace(s1, "_", "-", -1)
s3 := strings.Replace(s2, ".", "-", -1)
return s3
}

func (d *SolidfireSANStorageDriver) CreatePrepare(
Expand Down
9 changes: 9 additions & 0 deletions trident-installer/sample-input/backend-ontap-nas-economy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"version": 1,
"storageDriverName": "ontap-nas-economy",
"managementLIF": "10.0.0.1",
"dataLIF": "10.0.0.2",
"svm": "trident_svm",
"username": "cluster-admin",
"password": "password"
}

0 comments on commit 7df1731

Please sign in to comment.