Sync baremetal openstack with upstream

Change-Id: I125fc08e2cc4433aeaa470de48823dd4434c2030
Related-PROD: PROD-33018
diff --git a/openstack/baremetal/v1/drivers/doc.go b/openstack/baremetal/v1/drivers/doc.go
new file mode 100644
index 0000000..252c01b
--- /dev/null
+++ b/openstack/baremetal/v1/drivers/doc.go
@@ -0,0 +1,43 @@
+/*
+Package drivers contains the functionality for Listing drivers, driver details,
+driver properties and driver logical disk properties
+
+API reference: https://developer.openstack.org/api-ref/baremetal/#drivers-drivers
+
+Example to List Drivers
+
+	drivers.ListDrivers(client.ServiceClient(), drivers.ListDriversOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		driversList, err := drivers.ExtractDrivers(page)
+		if err != nil {
+			return false, err
+		}
+
+		for _, n := range driversList {
+			// Do something
+		}
+
+		return true, nil
+	})
+
+Example to Get single Driver Details
+
+	showDriverDetails, err := drivers.GetDriverDetails(client, "ipmi").Extract()
+	if err != nil {
+		panic(err)
+	}
+
+Example to Get single Driver Properties
+
+	showDriverProperties, err := drivers.GetDriverProperties(client, "ipmi").Extract()
+	if err != nil {
+		panic(err)
+	}
+
+Example to Get single Driver Logical Disk Properties
+
+	showDriverDiskProperties, err := drivers.GetDriverDiskProperties(client, "ipmi").Extract()
+	if err != nil {
+		panic(err)
+	}
+*/
+package drivers
diff --git a/openstack/baremetal/v1/drivers/requests.go b/openstack/baremetal/v1/drivers/requests.go
index 8e338e4..f50baa6 100644
--- a/openstack/baremetal/v1/drivers/requests.go
+++ b/openstack/baremetal/v1/drivers/requests.go
@@ -5,8 +5,66 @@
 	"gerrit.mcp.mirantis.net/debian/gophercloud.git/pagination"
 )
 
-func List(c *gophercloud.ServiceClient) pagination.Pager {
-	return pagination.NewPager(c, listURL(c), func(r pagination.PageResult) pagination.Page {
-		return DriverPage{pagination.SinglePageBase(r)}
+// ListDriversOptsBuilder allows extensions to add additional parameters to the
+// ListDrivers request.
+type ListDriversOptsBuilder interface {
+	ToListDriversOptsQuery() (string, error)
+}
+
+// ListDriversOpts defines query options that can be passed to ListDrivers
+type ListDriversOpts struct {
+	// Provide detailed information about the drivers
+	Detail bool `q:"detail"`
+
+	// Filter the list by the type of the driver
+	Type string `q:"type"`
+}
+
+// ToListDriversOptsQuery formats a ListOpts into a query string
+func (opts ListDriversOpts) ToListDriversOptsQuery() (string, error) {
+	q, err := gophercloud.BuildQueryString(opts)
+	return q.String(), err
+}
+
+// ListDrivers makes a request against the API to list all drivers
+func ListDrivers(client *gophercloud.ServiceClient, opts ListDriversOptsBuilder) pagination.Pager {
+	url := driversURL(client)
+	if opts != nil {
+		query, err := opts.ToListDriversOptsQuery()
+		if err != nil {
+			return pagination.Pager{Err: err}
+		}
+		url += query
+	}
+	return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
+		return DriverPage{pagination.LinkedPageBase{PageResult: r}}
 	})
 }
+
+// GetDriverDetails Shows details for a driver
+func GetDriverDetails(client *gophercloud.ServiceClient, driverName string) (r GetDriverResult) {
+	_, r.Err = client.Get(driverDetailsURL(client, driverName), &r.Body, &gophercloud.RequestOpts{
+		OkCodes: []int{200},
+	})
+	return
+}
+
+// GetDriverProperties Shows the required and optional parameters that
+// driverName expects to be supplied in the driver_info field for every
+// Node it manages
+func GetDriverProperties(client *gophercloud.ServiceClient, driverName string) (r GetPropertiesResult) {
+	_, r.Err = client.Get(driverPropertiesURL(client, driverName), &r.Body, &gophercloud.RequestOpts{
+		OkCodes: []int{200},
+	})
+	return
+}
+
+// GetDriverDiskProperties Show the required and optional parameters that
+// driverName expects to be supplied in the node’s raid_config field, if a
+// RAID configuration change is requested.
+func GetDriverDiskProperties(client *gophercloud.ServiceClient, driverName string) (r GetDiskPropertiesResult) {
+	_, r.Err = client.Get(driverDiskPropertiesURL(client, driverName), &r.Body, &gophercloud.RequestOpts{
+		OkCodes: []int{200},
+	})
+	return
+}
diff --git a/openstack/baremetal/v1/drivers/results.go b/openstack/baremetal/v1/drivers/results.go
index b3efacd..c453a09 100644
--- a/openstack/baremetal/v1/drivers/results.go
+++ b/openstack/baremetal/v1/drivers/results.go
@@ -5,59 +5,194 @@
 	"gerrit.mcp.mirantis.net/debian/gophercloud.git/pagination"
 )
 
-type commonResult struct {
+type driverResult struct {
 	gophercloud.Result
 }
 
-func (r commonResult) Extract() (*Driver, error) {
-	var d struct {
-		Driver *Driver `json:"driver"`
-	}
-	err := r.ExtractInto(&d)
-	return d.Driver, err
+// Extract interprets any driverResult as a Driver, if possible.
+func (r driverResult) Extract() (*Driver, error) {
+	var s Driver
+	err := r.ExtractInto(&s)
+	return &s, err
 }
 
-type GetResult struct {
-	commonResult
+func (r driverResult) ExtractInto(v interface{}) error {
+	return r.Result.ExtractIntoStructPtr(v, "")
 }
 
-type Link struct {
-	Href string `json:"href"`
-	Rel  string `json:"rel"`
+func ExtractDriversInto(r pagination.Page, v interface{}) error {
+	return r.(DriverPage).Result.ExtractIntoSlicePtr(v, "drivers")
 }
 
+// Driver represents a driver in the OpenStack Bare Metal API.
 type Driver struct {
-	Name       string   `json:"name"`
-	Type       string   `json:"type"`
-	Hosts      []string `json:"hosts"`
-	Links      []Link   `json:"links"`
-	Properties []Link   `json:"properties"`
+	// Name and Identifier of the driver
+	Name string `json:"name"`
+
+	// A list of active hosts that support this driver
+	Hosts []string `json:"hosts"`
+
+	// Type of this driver (“classic” or “dynamic”)
+	Type string `json:"type"`
+
+	// The default bios interface used for a node with a dynamic driver,
+	// if no bios interface is specified for the node.
+	DefaultBiosInterface string `json:"default_bios_interface"`
+
+	// The default boot interface used for a node with a dynamic driver,
+	// if no boot interface is specified for the node.
+	DefaultBootInterface string `json:"default_boot_interface"`
+
+	// The default console interface used for a node with a dynamic driver,
+	// if no console interface is specified for the node.
+	DefaultConsoleInterface string `json:"default_console_interface"`
+
+	// The default deploy interface used for a node with a dynamic driver,
+	// if no deploy interface is specified for the node.
+	DefaultDeployInterface string `json:"default_deploy_interface"`
+
+	// The default inspection interface used for a node with a dynamic driver,
+	// if no inspection interface is specified for the node.
+	DefaultInspectInterface string `json:"default_inspect_interface"`
+
+	// The default management interface used for a node with a dynamic driver,
+	// if no management interface is specified for the node.
+	DefaultManagementInterface string `json:"default_management_interface"`
+
+	// The default network interface used for a node with a dynamic driver,
+	// if no network interface is specified for the node.
+	DefaultNetworkInterface string `json:"default_network_interface"`
+
+	// The default power interface used for a node with a dynamic driver,
+	// if no power interface is specified for the node.
+	DefaultPowerInterface string `json:"default_power_interface"`
+
+	// The default RAID interface used for a node with a dynamic driver,
+	// if no RAID interface is specified for the node.
+	DefaultRaidInterface string `json:"default_raid_interface"`
+
+	// The default rescue interface used for a node with a dynamic driver,
+	// if no rescue interface is specified for the node.
+	DefaultRescueInterface string `json:"default_rescue_interface"`
+
+	// The default storage interface used for a node with a dynamic driver,
+	// if no storage interface is specified for the node.
+	DefaultStorageInterface string `json:"default_storage_interface"`
+
+	// The default vendor interface used for a node with a dynamic driver,
+	// if no vendor interface is specified for the node.
+	DefaultVendorInterface string `json:"default_vendor_interface"`
+
+	// The enabled bios interfaces for this driver.
+	EnabledBiosInterfaces []string `json:"enabled_bios_interfaces"`
+
+	// The enabled boot interfaces for this driver.
+	EnabledBootInterfaces []string `json:"enabled_boot_interfaces"`
+
+	// The enabled console interfaces for this driver.
+	EnabledConsoleInterface []string `json:"enabled_console_interfaces"`
+
+	// The enabled deploy interfaces for this driver.
+	EnabledDeployInterfaces []string `json:"enabled_deploy_interfaces"`
+
+	// The enabled inspection interfaces for this driver.
+	EnabledInspectInterfaces []string `json:"enabled_inspect_interfaces"`
+
+	// The enabled management interfaces for this driver.
+	EnabledManagementInterfaces []string `json:"enabled_management_interfaces"`
+
+	// The enabled network interfaces for this driver.
+	EnabledNetworkInterfaces []string `json:"enabled_network_interfaces"`
+
+	// The enabled power interfaces for this driver.
+	EnabledPowerInterfaces []string `json:"enabled_power_interfaces"`
+
+	// The enabled rescue interfaces for this driver.
+	EnabledRescueInterfaces []string `json:"enabled_rescue_interfaces"`
+
+	// The enabled RAID interfaces for this driver.
+	EnabledRaidInterfaces []string `json:"enabled_raid_interfaces"`
+
+	// The enabled storage interfaces for this driver.
+	EnabledStorageInterfaces []string `json:"enabled_storage_interfaces"`
+
+	// The enabled vendor interfaces for this driver.
+	EnabledVendorInterfaces []string `json:"enabled_vendor_interfaces"`
+
+	//A list of relative links. Includes the self and bookmark links.
+	Links []interface{} `json:"links"`
+
+	// A list of links to driver properties.
+	Properties []interface{} `json:"properties"`
 }
 
+// DriverPage abstracts the raw results of making a ListDrivers() request
+// against the API.
 type DriverPage struct {
-	pagination.SinglePageBase
+	pagination.LinkedPageBase
 }
 
+// IsEmpty returns true if a page contains no Driver results.
+func (r DriverPage) IsEmpty() (bool, error) {
+	s, err := ExtractDrivers(r)
+	return len(s) == 0, err
+}
+
+// NextPageURL uses the response's embedded link reference to navigate to the
+// next page of results.
 func (r DriverPage) NextPageURL() (string, error) {
-	var d struct {
+	var s struct {
 		Links []gophercloud.Link `json:"drivers_links"`
 	}
-	err := r.ExtractInto(&d)
+	err := r.ExtractInto(&s)
 	if err != nil {
 		return "", err
 	}
-	return gophercloud.ExtractNextURL(d.Links)
+	return gophercloud.ExtractNextURL(s.Links)
 }
 
-func (r DriverPage) IsEmpty() (bool, error) {
-	is, err := ExtractDrivers(r)
-	return len(is) == 0, err
-}
-
+// ExtractDrivers interprets the results of a single page from ListDrivers()
+// call, producing a slice of Driver entities.
 func ExtractDrivers(r pagination.Page) ([]Driver, error) {
-	var d struct {
-		Drivers []Driver `json:"drivers"`
-	}
-	err := (r.(DriverPage)).ExtractInto(&d)
-	return d.Drivers, err
+	var s []Driver
+	err := ExtractDriversInto(r, &s)
+	return s, err
+}
+
+// GetDriverResult is the response from a Get operation.
+// Call its Extract method to interpret it as a Driver.
+type GetDriverResult struct {
+	driverResult
+}
+
+// DriverProperties represents driver properties in the OpenStack Bare Metal API.
+type DriverProperties map[string]interface{}
+
+// Extract interprets any GetPropertiesResult as DriverProperties, if possible.
+func (r GetPropertiesResult) Extract() (*DriverProperties, error) {
+	var s DriverProperties
+	err := r.ExtractInto(&s)
+	return &s, err
+}
+
+// GetPropertiesResult is the response from a GetDriverProperties operation.
+// Call its Extract method to interpret it as DriverProperties.
+type GetPropertiesResult struct {
+	gophercloud.Result
+}
+
+// DiskProperties represents driver disk properties in the OpenStack Bare Metal API.
+type DiskProperties map[string]interface{}
+
+// Extract interprets any GetDiskPropertiesResult as DiskProperties, if possible.
+func (r GetDiskPropertiesResult) Extract() (*DiskProperties, error) {
+	var s DiskProperties
+	err := r.ExtractInto(&s)
+	return &s, err
+}
+
+// GetDiskPropertiesResult is the response from a GetDriverDiskProperties operation.
+// Call its Extract method to interpret it as DiskProperties.
+type GetDiskPropertiesResult struct {
+	gophercloud.Result
 }
diff --git a/openstack/baremetal/v1/drivers/testing/doc.go b/openstack/baremetal/v1/drivers/testing/doc.go
new file mode 100644
index 0000000..266af92
--- /dev/null
+++ b/openstack/baremetal/v1/drivers/testing/doc.go
@@ -0,0 +1,2 @@
+// Package testing contains drivers unit tests
+package testing
diff --git a/openstack/baremetal/v1/drivers/testing/fixtures.go b/openstack/baremetal/v1/drivers/testing/fixtures.go
new file mode 100644
index 0000000..7504030
--- /dev/null
+++ b/openstack/baremetal/v1/drivers/testing/fixtures.go
@@ -0,0 +1,407 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/baremetal/v1/drivers"
+	th "gerrit.mcp.mirantis.net/debian/gophercloud.git/testhelper"
+	"gerrit.mcp.mirantis.net/debian/gophercloud.git/testhelper/client"
+)
+
+// ListDriversBody contains the canned body of a drivers.ListDrivers response, without details.
+const ListDriversBody = `
+{
+  "drivers": [
+    {
+      "hosts": [
+        "897ab1dad809"
+      ],
+      "links": [
+        {
+          "href": "http://127.0.0.1:6385/v1/drivers/agent_ipmitool",
+          "rel": "self"
+        },
+        {
+          "href": "http://127.0.0.1:6385/drivers/agent_ipmitool",
+          "rel": "bookmark"
+        }
+      ],
+      "name": "agent_ipmitool",
+      "properties": [
+        {
+          "href": "http://127.0.0.1:6385/v1/drivers/agent_ipmitool/properties",
+          "rel": "self"
+        },
+        {
+          "href": "http://127.0.0.1:6385/drivers/agent_ipmitool/properties",
+          "rel": "bookmark"
+        }
+      ],
+      "type": "classic"
+    },
+    {
+      "hosts": [
+        "897ab1dad809"
+      ],
+      "links": [
+        {
+          "href": "http://127.0.0.1:6385/v1/drivers/fake",
+          "rel": "self"
+        },
+        {
+          "href": "http://127.0.0.1:6385/drivers/fake",
+          "rel": "bookmark"
+        }
+      ],
+      "name": "fake",
+      "properties": [
+        {
+          "href": "http://127.0.0.1:6385/v1/drivers/fake/properties",
+          "rel": "self"
+        },
+        {
+          "href": "http://127.0.0.1:6385/drivers/fake/properties",
+          "rel": "bookmark"
+        }
+      ],
+      "type": "classic"
+    },
+    {
+      "hosts": [
+        "897ab1dad809"
+      ],
+      "links": [
+        {
+          "href": "http://127.0.0.1:6385/v1/drivers/ipmi",
+          "rel": "self"
+        },
+        {
+          "href": "http://127.0.0.1:6385/drivers/ipmi",
+          "rel": "bookmark"
+        }
+      ],
+      "name": "ipmi",
+      "properties": [
+        {
+          "href": "http://127.0.0.1:6385/v1/drivers/ipmi/properties",
+          "rel": "self"
+        },
+        {
+          "href": "http://127.0.0.1:6385/drivers/ipmi/properties",
+          "rel": "bookmark"
+        }
+      ],
+      "type": "dynamic"
+    }
+  ]
+}
+`
+const SingleDriverDetails = `
+{
+  "default_bios_interface": "no-bios",
+  "default_boot_interface": "pxe",
+  "default_console_interface": "no-console",
+  "default_deploy_interface": "iscsi",
+  "default_inspect_interface": "no-inspect",
+  "default_management_interface": "ipmitool",
+  "default_network_interface": "flat",
+  "default_power_interface": "ipmitool",
+  "default_raid_interface": "no-raid",
+  "default_rescue_interface": "no-rescue",
+  "default_storage_interface": "noop",
+  "default_vendor_interface": "no-vendor",
+  "enabled_bios_interfaces": [
+    "no-bios"
+  ],
+  "enabled_boot_interfaces": [
+    "pxe"
+  ],
+  "enabled_console_interfaces": [
+    "no-console"
+  ],
+  "enabled_deploy_interfaces": [
+    "iscsi",
+    "direct"
+  ],
+  "enabled_inspect_interfaces": [
+    "no-inspect"
+  ],
+  "enabled_management_interfaces": [
+    "ipmitool"
+  ],
+  "enabled_network_interfaces": [
+    "flat",
+    "noop"
+  ],
+  "enabled_power_interfaces": [
+    "ipmitool"
+  ],
+  "enabled_raid_interfaces": [
+    "no-raid",
+    "agent"
+  ],
+  "enabled_rescue_interfaces": [
+    "no-rescue"
+  ],
+  "enabled_storage_interfaces": [
+    "noop"
+  ],
+  "enabled_vendor_interfaces": [
+    "no-vendor"
+  ],
+  "hosts": [
+    "897ab1dad809"
+  ],
+  "links": [
+    {
+      "href": "http://127.0.0.1:6385/v1/drivers/ipmi",
+      "rel": "self"
+    },
+    {
+      "href": "http://127.0.0.1:6385/drivers/ipmi",
+      "rel": "bookmark"
+    }
+  ],
+  "name": "ipmi",
+  "properties": [
+    {
+      "href": "http://127.0.0.1:6385/v1/drivers/ipmi/properties",
+      "rel": "self"
+    },
+    {
+      "href": "http://127.0.0.1:6385/drivers/ipmi/properties",
+      "rel": "bookmark"
+    }
+  ],
+  "type": "dynamic"
+}
+`
+
+const SingleDriverProperties = `
+{
+  "deploy_forces_oob_reboot": "Whether Ironic should force a reboot of the Node via the out-of-band channel after deployment is complete. Provides compatibility with older deploy ramdisks. Defaults to False. Optional.",
+  "deploy_kernel": "UUID (from Glance) of the deployment kernel. Required.",
+  "deploy_ramdisk": "UUID (from Glance) of the ramdisk that is mounted at boot time. Required.",
+  "image_http_proxy": "URL of a proxy server for HTTP connections. Optional.",
+  "image_https_proxy": "URL of a proxy server for HTTPS connections. Optional.",
+  "image_no_proxy": "A comma-separated list of host names, IP addresses and domain names (with optional :port) that will be excluded from proxying. To denote a domain name, use a dot to prefix the domain name. This value will be ignored if ` + "``image_http_proxy`` and ``image_https_proxy``" + ` are not specified. Optional.",
+  "ipmi_address": "IP address or hostname of the node. Required.",
+  "ipmi_bridging": "bridging_type; default is \"no\". One of \"single\", \"dual\", \"no\". Optional.",
+  "ipmi_disable_boot_timeout": "By default ironic will send a raw IPMI command to disable the 60 second timeout for booting.  Setting this option to False will NOT send that command; default value is True. Optional.",
+  "ipmi_force_boot_device": "Whether Ironic should specify the boot device to the BMC each time the server is turned on, eg. because the BMC is not capable of remembering the selected boot device across power cycles; default value is False. Optional.",
+  "ipmi_local_address": "local IPMB address for bridged requests. Used only if ipmi_bridging is set to \"single\" or \"dual\". Optional.",
+  "ipmi_password": "password. Optional.",
+  "ipmi_port": "remote IPMI RMCP port. Optional.",
+  "ipmi_priv_level": "privilege level; default is ADMINISTRATOR. One of ADMINISTRATOR, CALLBACK, OPERATOR, USER. Optional.",
+  "ipmi_protocol_version": "the version of the IPMI protocol; default is \"2.0\". One of \"1.5\", \"2.0\". Optional.",
+  "ipmi_target_address": "destination address for bridged request. Required only if ipmi_bridging is set to \"single\" or \"dual\".",
+  "ipmi_target_channel": "destination channel for bridged request. Required only if ipmi_bridging is set to \"single\" or \"dual\".",
+  "ipmi_terminal_port": "node's UDP port to connect to. Only required for console access.",
+  "ipmi_transit_address": "transit address for bridged request. Required only if ipmi_bridging is set to \"dual\".",
+  "ipmi_transit_channel": "transit channel for bridged request. Required only if ipmi_bridging is set to \"dual\".",
+  "ipmi_username": "username; default is NULL user. Optional."
+}
+`
+
+const SingleDriverDiskProperties = `
+{
+  "controller": "Controller to use for this logical disk. If not specified, the driver will choose a suitable RAID controller on the bare metal node. Optional.",
+  "disk_type": "The type of disk preferred. Valid values are 'hdd' and 'ssd'. If this is not specified, disk type will not be a selection criterion for choosing backing physical disks. Optional.",
+  "interface_type": "The interface type of disk. Valid values are 'sata', 'scsi' and 'sas'. If this is not specified, interface type will not be a selection criterion for choosing backing physical disks. Optional.",
+  "is_root_volume": "Specifies whether this disk is a root volume. By default, this is False. Optional.",
+  "number_of_physical_disks": "Number of physical disks to use for this logical disk. By default, the driver uses the minimum number of disks required for that RAID level. Optional.",
+  "physical_disks": "The physical disks to use for this logical disk. If not specified, the driver will choose suitable physical disks to use. Optional.",
+  "raid_level": "RAID level for the logical disk. Valid values are 'JBOD', '0', '1', '2', '5', '6', '1+0', '5+0' and '6+0'. Required.",
+  "share_physical_disks": "Specifies whether other logical disks can share physical disks with this logical disk. By default, this is False. Optional.",
+  "size_gb": "Size in GiB (Integer) for the logical disk. Use 'MAX' as size_gb if this logical disk is supposed to use the rest of the space available. Required.",
+  "volume_name": "Name of the volume to be created. If this is not specified, it will be auto-generated. Optional."
+}
+`
+
+var (
+	DriverAgentIpmitool = drivers.Driver{
+		Name:  "agent_ipmitool",
+		Type:  "classic",
+		Hosts: []string{"897ab1dad809"},
+		Links: []interface{}{
+			map[string]interface{}{
+				"href": "http://127.0.0.1:6385/v1/drivers/agent_ipmitool",
+				"rel":  "self",
+			},
+			map[string]interface{}{
+				"href": "http://127.0.0.1:6385/drivers/agent_ipmitool",
+				"rel":  "bookmark",
+			},
+		},
+		Properties: []interface{}{
+			map[string]interface{}{
+				"href": "http://127.0.0.1:6385/v1/drivers/agent_ipmitool/properties",
+				"rel":  "self",
+			},
+			map[string]interface{}{
+				"href": "http://127.0.0.1:6385/drivers/agent_ipmitool/properties",
+				"rel":  "bookmark",
+			},
+		},
+	}
+
+	DriverFake = drivers.Driver{
+		Name:  "fake",
+		Type:  "classic",
+		Hosts: []string{"897ab1dad809"},
+		Links: []interface{}{
+			map[string]interface{}{
+				"href": "http://127.0.0.1:6385/v1/drivers/fake",
+				"rel":  "self",
+			},
+			map[string]interface{}{
+				"href": "http://127.0.0.1:6385/drivers/fake",
+				"rel":  "bookmark",
+			},
+		},
+		Properties: []interface{}{
+			map[string]interface{}{
+				"href": "http://127.0.0.1:6385/v1/drivers/fake/properties",
+				"rel":  "self",
+			},
+			map[string]interface{}{
+				"href": "http://127.0.0.1:6385/drivers/fake/properties",
+				"rel":  "bookmark",
+			},
+		},
+	}
+
+	DriverIpmi = drivers.Driver{
+		Name:                        "ipmi",
+		Type:                        "dynamic",
+		Hosts:                       []string{"897ab1dad809"},
+		DefaultBiosInterface:        "no-bios",
+		DefaultBootInterface:        "pxe",
+		DefaultConsoleInterface:     "no-console",
+		DefaultDeployInterface:      "iscsi",
+		DefaultInspectInterface:     "no-inspect",
+		DefaultManagementInterface:  "ipmitool",
+		DefaultNetworkInterface:     "flat",
+		DefaultPowerInterface:       "ipmitool",
+		DefaultRaidInterface:        "no-raid",
+		DefaultRescueInterface:      "no-rescue",
+		DefaultStorageInterface:     "noop",
+		DefaultVendorInterface:      "no-vendor",
+		EnabledBiosInterfaces:       []string{"no-bios"},
+		EnabledBootInterfaces:       []string{"pxe"},
+		EnabledConsoleInterface:     []string{"no-console"},
+		EnabledDeployInterfaces:     []string{"iscsi", "direct"},
+		EnabledInspectInterfaces:    []string{"no-inspect"},
+		EnabledManagementInterfaces: []string{"ipmitool"},
+		EnabledNetworkInterfaces:    []string{"flat", "noop"},
+		EnabledPowerInterfaces:      []string{"ipmitool"},
+		EnabledRescueInterfaces:     []string{"no-rescue"},
+		EnabledRaidInterfaces:       []string{"no-raid", "agent"},
+		EnabledStorageInterfaces:    []string{"noop"},
+		EnabledVendorInterfaces:     []string{"no-vendor"},
+		Links: []interface{}{
+			map[string]interface{}{
+				"href": "http://127.0.0.1:6385/v1/drivers/ipmi",
+				"rel":  "self",
+			},
+			map[string]interface{}{
+				"href": "http://127.0.0.1:6385/drivers/ipmi",
+				"rel":  "bookmark",
+			},
+		},
+		Properties: []interface{}{
+			map[string]interface{}{
+				"href": "http://127.0.0.1:6385/v1/drivers/ipmi/properties",
+				"rel":  "self",
+			},
+			map[string]interface{}{
+				"href": "http://127.0.0.1:6385/drivers/ipmi/properties",
+				"rel":  "bookmark",
+			},
+		},
+	}
+
+	DriverIpmiToolProperties = drivers.DriverProperties{
+		"deploy_forces_oob_reboot":  "Whether Ironic should force a reboot of the Node via the out-of-band channel after deployment is complete. Provides compatibility with older deploy ramdisks. Defaults to False. Optional.",
+		"deploy_kernel":             "UUID (from Glance) of the deployment kernel. Required.",
+		"deploy_ramdisk":            "UUID (from Glance) of the ramdisk that is mounted at boot time. Required.",
+		"image_http_proxy":          "URL of a proxy server for HTTP connections. Optional.",
+		"image_https_proxy":         "URL of a proxy server for HTTPS connections. Optional.",
+		"image_no_proxy":            "A comma-separated list of host names, IP addresses and domain names (with optional :port) that will be excluded from proxying. To denote a domain name, use a dot to prefix the domain name. This value will be ignored if ``image_http_proxy`` and ``image_https_proxy`` are not specified. Optional.",
+		"ipmi_address":              "IP address or hostname of the node. Required.",
+		"ipmi_bridging":             "bridging_type; default is \"no\". One of \"single\", \"dual\", \"no\". Optional.",
+		"ipmi_disable_boot_timeout": "By default ironic will send a raw IPMI command to disable the 60 second timeout for booting.  Setting this option to False will NOT send that command; default value is True. Optional.",
+		"ipmi_force_boot_device":    "Whether Ironic should specify the boot device to the BMC each time the server is turned on, eg. because the BMC is not capable of remembering the selected boot device across power cycles; default value is False. Optional.",
+		"ipmi_local_address":        "local IPMB address for bridged requests. Used only if ipmi_bridging is set to \"single\" or \"dual\". Optional.",
+		"ipmi_password":             "password. Optional.",
+		"ipmi_port":                 "remote IPMI RMCP port. Optional.",
+		"ipmi_priv_level":           "privilege level; default is ADMINISTRATOR. One of ADMINISTRATOR, CALLBACK, OPERATOR, USER. Optional.",
+		"ipmi_protocol_version":     "the version of the IPMI protocol; default is \"2.0\". One of \"1.5\", \"2.0\". Optional.",
+		"ipmi_target_address":       "destination address for bridged request. Required only if ipmi_bridging is set to \"single\" or \"dual\".",
+		"ipmi_target_channel":       "destination channel for bridged request. Required only if ipmi_bridging is set to \"single\" or \"dual\".",
+		"ipmi_terminal_port":        "node's UDP port to connect to. Only required for console access.",
+		"ipmi_transit_address":      "transit address for bridged request. Required only if ipmi_bridging is set to \"dual\".",
+		"ipmi_transit_channel":      "transit channel for bridged request. Required only if ipmi_bridging is set to \"dual\".",
+		"ipmi_username":             "username; default is NULL user. Optional.",
+	}
+
+	DriverIpmiToolDisk = drivers.DiskProperties{
+		"controller":               "Controller to use for this logical disk. If not specified, the driver will choose a suitable RAID controller on the bare metal node. Optional.",
+		"disk_type":                "The type of disk preferred. Valid values are 'hdd' and 'ssd'. If this is not specified, disk type will not be a selection criterion for choosing backing physical disks. Optional.",
+		"interface_type":           "The interface type of disk. Valid values are 'sata', 'scsi' and 'sas'. If this is not specified, interface type will not be a selection criterion for choosing backing physical disks. Optional.",
+		"is_root_volume":           "Specifies whether this disk is a root volume. By default, this is False. Optional.",
+		"number_of_physical_disks": "Number of physical disks to use for this logical disk. By default, the driver uses the minimum number of disks required for that RAID level. Optional.",
+		"physical_disks":           "The physical disks to use for this logical disk. If not specified, the driver will choose suitable physical disks to use. Optional.",
+		"raid_level":               "RAID level for the logical disk. Valid values are 'JBOD', '0', '1', '2', '5', '6', '1+0', '5+0' and '6+0'. Required.",
+		"share_physical_disks":     "Specifies whether other logical disks can share physical disks with this logical disk. By default, this is False. Optional.",
+		"size_gb":                  "Size in GiB (Integer) for the logical disk. Use 'MAX' as size_gb if this logical disk is supposed to use the rest of the space available. Required.",
+		"volume_name":              "Name of the volume to be created. If this is not specified, it will be auto-generated. Optional.",
+	}
+)
+
+// HandleListDriversSuccessfully sets up the test server to respond to a drivers ListDrivers request.
+func HandleListDriversSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/drivers", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+
+		fmt.Fprintf(w, ListDriversBody)
+	})
+}
+
+// HandleGetDriverDetailsSuccessfully sets up the test server to respond to a drivers GetDriverDetails request.
+func HandleGetDriverDetailsSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/drivers/ipmi", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, SingleDriverDetails)
+	})
+}
+
+// HandleGetDriverPropertiesSuccessfully sets up the test server to respond to a drivers GetDriverProperties request.
+func HandleGetDriverPropertiesSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/drivers/agent_ipmitool/properties", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, SingleDriverProperties)
+	})
+}
+
+// HandleGetDriverDiskPropertiesSuccessfully sets up the test server to respond to a drivers GetDriverDiskProperties request.
+func HandleGetDriverDiskPropertiesSuccessfully(t *testing.T) {
+	th.Mux.HandleFunc("/drivers/agent_ipmitool/raid/logical_disk_properties", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
+		th.TestHeader(t, r, "Accept", "application/json")
+
+		fmt.Fprintf(w, SingleDriverDiskProperties)
+	})
+}
diff --git a/openstack/baremetal/v1/drivers/testing/requests_test.go b/openstack/baremetal/v1/drivers/testing/requests_test.go
new file mode 100644
index 0000000..3a9ddf9
--- /dev/null
+++ b/openstack/baremetal/v1/drivers/testing/requests_test.go
@@ -0,0 +1,84 @@
+package testing
+
+import (
+	"testing"
+
+	"gerrit.mcp.mirantis.net/debian/gophercloud.git/openstack/baremetal/v1/drivers"
+	"gerrit.mcp.mirantis.net/debian/gophercloud.git/pagination"
+	th "gerrit.mcp.mirantis.net/debian/gophercloud.git/testhelper"
+	"gerrit.mcp.mirantis.net/debian/gophercloud.git/testhelper/client"
+)
+
+func TestListDrivers(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleListDriversSuccessfully(t)
+
+	pages := 0
+	err := drivers.ListDrivers(client.ServiceClient(), drivers.ListDriversOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+		pages++
+
+		actual, err := drivers.ExtractDrivers(page)
+		if err != nil {
+			return false, err
+		}
+
+		if len(actual) != 3 {
+			t.Fatalf("Expected 3 drivers, got %d", len(actual))
+		}
+
+		th.CheckDeepEquals(t, DriverAgentIpmitool, actual[0])
+		th.CheckDeepEquals(t, DriverFake, actual[1])
+		th.AssertEquals(t, "ipmi", actual[2].Name)
+
+		return true, nil
+	})
+
+	th.AssertNoErr(t, err)
+
+	if pages != 1 {
+		t.Errorf("Expected 1 page, saw %d", pages)
+	}
+}
+
+func TestGetDriverDetails(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetDriverDetailsSuccessfully(t)
+
+	c := client.ServiceClient()
+	actual, err := drivers.GetDriverDetails(c, "ipmi").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, DriverIpmi, *actual)
+}
+
+func TestGetDriverProperties(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetDriverPropertiesSuccessfully(t)
+
+	c := client.ServiceClient()
+	actual, err := drivers.GetDriverProperties(c, "agent_ipmitool").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, DriverIpmiToolProperties, *actual)
+}
+
+func TestGetDriverDiskProperties(t *testing.T) {
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleGetDriverDiskPropertiesSuccessfully(t)
+
+	c := client.ServiceClient()
+	actual, err := drivers.GetDriverDiskProperties(c, "agent_ipmitool").Extract()
+	if err != nil {
+		t.Fatalf("Unexpected Get error: %v", err)
+	}
+
+	th.CheckDeepEquals(t, DriverIpmiToolDisk, *actual)
+}
diff --git a/openstack/baremetal/v1/drivers/urls.go b/openstack/baremetal/v1/drivers/urls.go
index 0996543..c8ebbee 100644
--- a/openstack/baremetal/v1/drivers/urls.go
+++ b/openstack/baremetal/v1/drivers/urls.go
@@ -2,6 +2,18 @@
 
 import "gerrit.mcp.mirantis.net/debian/gophercloud.git"
 
-func listURL(c *gophercloud.ServiceClient) string {
-	return c.ServiceURL("drivers")
+func driversURL(client *gophercloud.ServiceClient) string {
+	return client.ServiceURL("drivers")
+}
+
+func driverDetailsURL(client *gophercloud.ServiceClient, driverName string) string {
+	return client.ServiceURL("drivers", driverName)
+}
+
+func driverPropertiesURL(client *gophercloud.ServiceClient, driverName string) string {
+	return client.ServiceURL("drivers", driverName, "properties")
+}
+
+func driverDiskPropertiesURL(client *gophercloud.ServiceClient, driverName string) string {
+	return client.ServiceURL("drivers", driverName, "raid", "logical_disk_properties")
 }