diff --git a/doc/source/deploy/drivers.rst b/doc/source/deploy/drivers.rst
index 67ea119703..ef78f3221d 100644
--- a/doc/source/deploy/drivers.rst
+++ b/doc/source/deploy/drivers.rst
@@ -105,3 +105,12 @@ iBoot driver
   :maxdepth: 1
 
   ../drivers/iboot
+
+
+CIMC driver
+------------
+
+.. toctree::
+  :maxdepth: 1
+
+  ../drivers/cimc
diff --git a/doc/source/drivers/cimc.rst b/doc/source/drivers/cimc.rst
new file mode 100644
index 0000000000..b7c22efa9a
--- /dev/null
+++ b/doc/source/drivers/cimc.rst
@@ -0,0 +1,95 @@
+.. _CIMC:
+
+============
+CIMC drivers
+============
+
+Overview
+========
+The CIMC drivers are targeted for standalone Cisco UCS C series servers.
+These drivers enable you to take advantage of CIMC by using the
+python SDK.
+
+``pxe_iscsi_cimc`` driver uses PXE boot + iSCSI deploy (just like ``pxe_ipmitool``
+driver) to deploy the image and uses CIMC to do all management operations on
+the baremetal node (instead of using IPMI).
+
+``pxe_agent_cimc`` driver uses PXE boot + Agent deploy (just like ``agent_ipmitool``
+and ``agent_ipminative`` drivers.) to deploy the image and uses CIMC to do all
+management operations on the baremetal node (instead of using IPMI). Unlike with
+iSCSI deploy in Agent deploy, the ramdisk is responsible for writing the image to
+the disk, instead of the conductor.
+
+Prerequisites
+=============
+
+* ``ImcSdk`` is a python SDK for the CIMC HTTP/HTTPS XML API used to control
+  CIMC.
+
+Install the ``ImcSdk`` module
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. note::
+
+  Install the ``ImcSdk`` module on the Ironic conductor node. Required version is
+  0.7.1.
+
+#. Download the tar.gz from: https://communities.cisco.com/docs/DOC-56257
+
+#. Unpack it::
+
+   $ tar xvf ImcSdk-0.7.1.tar.gz
+
+#. Install it::
+
+   $ cd ImcSdk-0.7.1
+   $ python setup.py install
+
+Tested Platforms
+~~~~~~~~~~~~~~~~
+This driver works with UCS C-Series servers and has been tested with:
+
+* UCS C240M3S
+
+Configuring and Enabling the driver
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1. Add ``pxe_cimc`` and/or ``agent_cimc`` to the list of ``enabled_drivers`` in
+   ``/etc/ironic/ironic.conf``.  For example::
+
+    enabled_drivers = pxe_ipmitool,pxe_cimc,agent_cimc
+
+2. Restart the Ironic conductor service:
+
+   For Ubuntu/Debian systems::
+
+      $ sudo service ironic-conductor restart
+
+   or for RHEL/CentOS/Fedora::
+
+      $ sudo systemctl restart openstack-ironic-conductor
+
+Registering Standalone UCS node in Ironic
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Nodes configured for CIMC driver should have the ``driver`` property set to
+``pxe_iscsi_cimc`` or ``pxe_agent_cimc``.  The following configuration values are
+also required in ``driver_info``:
+
+- ``cimc_address``: IP address or hostname for CIMC
+- ``cimc_username``: CIMC login user name
+- ``cimc_password``: CIMC login password for the above CIMC user.
+- ``deploy_kernel``: The Glance UUID of the deployment kernel.
+- ``deploy_ramdisk``: The Glance UUID of the deployment ramdisk.
+
+The following sequence of commands can be used to enroll a UCS Standalone node.
+
+  Create Node::
+
+    ironic node-create -d <pxe_cimc/agent_cimc> -i cimc_address=<CIMC hostname/ip-address> -i cimc_username=<cimc_username> -i cimc_password=<cimc_password> -i deploy_kernel=<glance_uuid_of_deploy_kernel> -i deploy_ramdisk=<glance_uuid_of_deploy_ramdisk> -p cpus=<number_of_cpus> -p memory_mb=<memory_size_in_MB> -p local_gb=<local_disk_size_in_GB> -p cpu_arch=<cpu_arch>
+
+  The above command 'ironic node-create' will return UUID of the node, which is the value of $NODE in the following command.
+
+  Associate port with the node created::
+
+    ironic port-create -n $NODE -a <MAC_address_of_Ucs_server's_NIC>
+
+For more information about enrolling nodes see "Enrolling a node" in the :ref:`install-guide`
diff --git a/driver-requirements.txt b/driver-requirements.txt
index 108e92fc01..692c7e062a 100644
--- a/driver-requirements.txt
+++ b/driver-requirements.txt
@@ -26,3 +26,6 @@ UcsSdk==0.8.2.2
 # Refer documentation on how to install and configure this:
 # http://docs.openstack.org/developer/ironic/drivers/vbox.html
 pyremotevbox>=0.5.0
+
+# The CIMC drivers use the Cisco IMC SDK version 0.7.1, which is avaliable from
+# https://communities.cisco.com/docs/DOC-37174
diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample
index dbbf766cc5..932888570f 100644
--- a/etc/ironic/ironic.conf.sample
+++ b/etc/ironic/ironic.conf.sample
@@ -453,6 +453,21 @@
 #public_endpoint=<None>
 
 
+[cimc]
+
+#
+# Options defined in ironic.drivers.modules.cimc.power
+#
+
+# Number of times a power operation needs to be retried
+# (integer value)
+#max_retry=6
+
+# Amount of time in seconds to wait in between power
+# operations (integer value)
+#action_interval=10
+
+
 [cisco_ucs]
 
 #
diff --git a/ironic/common/exception.py b/ironic/common/exception.py
index afa140ddaf..3eb83cbaaf 100644
--- a/ironic/common/exception.py
+++ b/ironic/common/exception.py
@@ -590,3 +590,7 @@ class WolOperationError(IronicException):
 class ImageUploadFailed(IronicException):
     message = _("Failed to upload %(image_name)s image to web server "
                 "%(web_server)s, reason: %(reason)s")
+
+
+class CIMCException(IronicException):
+    message = _("Cisco IMC exception occured for node %(node)s: %(error)s")
diff --git a/ironic/drivers/agent.py b/ironic/drivers/agent.py
index 088c2e1181..34446ed6f8 100644
--- a/ironic/drivers/agent.py
+++ b/ironic/drivers/agent.py
@@ -18,6 +18,8 @@ from ironic.common import exception
 from ironic.common.i18n import _
 from ironic.drivers import base
 from ironic.drivers.modules import agent
+from ironic.drivers.modules.cimc import management as cimc_mgmt
+from ironic.drivers.modules.cimc import power as cimc_power
 from ironic.drivers.modules import ipminative
 from ironic.drivers.modules import ipmitool
 from ironic.drivers.modules import pxe
@@ -157,3 +159,26 @@ class AgentAndUcsDriver(base.BaseDriver):
         self.deploy = agent.AgentDeploy()
         self.management = ucs_mgmt.UcsManagement()
         self.vendor = agent.AgentVendorInterface()
+
+
+class AgentAndCIMCDriver(base.BaseDriver):
+    """Agent + Cisco CIMC driver.
+
+    This driver implements the `core` functionality, combining
+    :class:ironic.drivers.modules.cimc.power.Power for power
+    on/off and reboot with
+    :class:'ironic.driver.modules.agent.AgentDeploy' (for image deployment.)
+    Implementations are in those respective classes;
+    this class is merely the glue between them.
+    """
+
+    def __init__(self):
+        if not importutils.try_import('ImcSdk'):
+            raise exception.DriverLoadError(
+                driver=self.__class__.__name__,
+                reason=_("Unable to import ImcSdk library"))
+        self.power = cimc_power.Power()
+        self.boot = pxe.PXEBoot()
+        self.deploy = agent.AgentDeploy()
+        self.management = cimc_mgmt.CIMCManagement()
+        self.vendor = agent.AgentVendorInterface()
diff --git a/ironic/drivers/fake.py b/ironic/drivers/fake.py
index da4644935d..b139148961 100644
--- a/ironic/drivers/fake.py
+++ b/ironic/drivers/fake.py
@@ -25,6 +25,8 @@ from ironic.drivers import base
 from ironic.drivers.modules import agent
 from ironic.drivers.modules.amt import management as amt_mgmt
 from ironic.drivers.modules.amt import power as amt_power
+from ironic.drivers.modules.cimc import management as cimc_mgmt
+from ironic.drivers.modules.cimc import power as cimc_power
 from ironic.drivers.modules.drac import management as drac_mgmt
 from ironic.drivers.modules.drac import power as drac_power
 from ironic.drivers.modules import fake
@@ -270,6 +272,19 @@ class FakeUcsDriver(base.BaseDriver):
         self.management = ucs_mgmt.UcsManagement()
 
 
+class FakeCIMCDriver(base.BaseDriver):
+    """Fake CIMC driver."""
+
+    def __init__(self):
+        if not importutils.try_import('ImcSdk'):
+            raise exception.DriverLoadError(
+                driver=self.__class__.__name__,
+                reason=_("Unable to import ImcSdk library"))
+        self.power = cimc_power.Power()
+        self.deploy = fake.FakeDeploy()
+        self.management = cimc_mgmt.CIMCManagement()
+
+
 class FakeWakeOnLanDriver(base.BaseDriver):
     """Fake Wake-On-Lan driver."""
 
diff --git a/ironic/drivers/modules/cimc/__init__.py b/ironic/drivers/modules/cimc/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/ironic/drivers/modules/cimc/common.py b/ironic/drivers/modules/cimc/common.py
new file mode 100644
index 0000000000..1340477d4c
--- /dev/null
+++ b/ironic/drivers/modules/cimc/common.py
@@ -0,0 +1,87 @@
+# Copyright 2015, Cisco Systems.
+#
+# 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.
+
+from contextlib import contextmanager
+
+from oslo_log import log as logging
+from oslo_utils import importutils
+
+from ironic.common import exception
+from ironic.drivers.modules import deploy_utils
+
+REQUIRED_PROPERTIES = {
+    'cimc_address': _('IP or Hostname of the CIMC. Required.'),
+    'cimc_username': _('CIMC Manager admin username. Required.'),
+    'cimc_password': _('CIMC Manager password. Required.'),
+}
+
+COMMON_PROPERTIES = REQUIRED_PROPERTIES
+
+imcsdk = importutils.try_import('ImcSdk')
+
+LOG = logging.getLogger(__name__)
+
+
+def parse_driver_info(node):
+    """Parses and creates Cisco driver info
+
+    :param node: An Ironic node object.
+    :returns: dictionary that contains node.driver_info parameter/values.
+    :raises: MissingParameterValue if any required parameters are missing.
+    """
+
+    info = {}
+    for param in REQUIRED_PROPERTIES:
+        info[param] = node.driver_info.get(param)
+    error_msg = (_("%s driver requires these parameters to be set in the "
+                   "node's driver_info.") %
+                 node.driver)
+    deploy_utils.check_for_missing_params(info, error_msg)
+    return info
+
+
+def handle_login(task, handle, info):
+    """Login to the CIMC handle.
+
+    Run login on the CIMC handle, catching any ImcException and reraising
+    it as an ironic CIMCException.
+
+    :param handle: A CIMC handle.
+    :param info: A list of driver info as produced by parse_driver_info.
+    :raises: CIMCException if there error logging in.
+    """
+    try:
+        handle.login(info['cimc_address'],
+                     info['cimc_username'],
+                     info['cimc_password'])
+    except imcsdk.ImcException as e:
+        raise exception.CIMCException(node=task.node.uuid, error=e)
+
+
+@contextmanager
+def cimc_handle(task):
+    """Context manager for creating a CIMC handle and logging into it
+
+    :param task: The current task object.
+    :raises: CIMCException if login fails
+    :yields: A CIMC Handle for the node in the task.
+    """
+    info = parse_driver_info(task.node)
+    handle = imcsdk.ImcHandle()
+
+    handle_login(task, handle, info)
+    try:
+        yield handle
+    finally:
+        handle.logout()
diff --git a/ironic/drivers/modules/cimc/management.py b/ironic/drivers/modules/cimc/management.py
new file mode 100644
index 0000000000..fc8dfcee80
--- /dev/null
+++ b/ironic/drivers/modules/cimc/management.py
@@ -0,0 +1,166 @@
+# Copyright 2015, Cisco Systems.
+#
+# 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.
+
+from oslo_log import log as logging
+from oslo_utils import importutils
+
+from ironic.common import boot_devices
+from ironic.common import exception
+from ironic.drivers import base
+from ironic.drivers.modules.cimc import common
+
+imcsdk = importutils.try_import('ImcSdk')
+
+LOG = logging.getLogger(__name__)
+
+CIMC_TO_IRONIC_BOOT_DEVICE = {
+    'storage-read-write': boot_devices.DISK,
+    'lan-read-only': boot_devices.PXE,
+    'vm-read-only': boot_devices.CDROM
+}
+
+IRONIC_TO_CIMC_BOOT_DEVICE = {
+    boot_devices.DISK: ('lsbootStorage', 'storage-read-write',
+                        'storage', 'read-write'),
+    boot_devices.PXE: ('lsbootLan', 'lan-read-only',
+                       'lan', 'read-only'),
+    boot_devices.CDROM: ('lsbootVirtualMedia', 'vm-read-only',
+                         'virtual-media', 'read-only')
+}
+
+
+class CIMCManagement(base.ManagementInterface):
+
+    def get_properties(self):
+        """Return the properties of the interface.
+
+        :returns: dictionary of <property name>:<property description> entries.
+        """
+        return common.COMMON_PROPERTIES
+
+    def validate(self, task):
+        """Check if node.driver_info contains the required CIMC credentials.
+
+        :param task: a TaskManager instance.
+        :raises: InvalidParameterValue if required CIMC credentials are
+                 missing.
+        """
+        common.parse_driver_info(task.node)
+
+    def get_supported_boot_devices(self, task):
+        """Get a list of the supported boot devices.
+
+        :param task: a task from TaskManager.
+        :returns: A list with the supported boot devices defined
+                  in :mod:`ironic.common.boot_devices`.
+        """
+        return list(CIMC_TO_IRONIC_BOOT_DEVICE.values())
+
+    def get_boot_device(self, task):
+        """Get the current boot device for a node.
+
+        Provides the current boot device of the node. Be aware that not
+        all drivers support this.
+
+        :param task: a task from TaskManager.
+        :raises: MissingParameterValue if a required parameter is missing
+        :raises: CIMCException if there is an error from CIMC
+        :returns: a dictionary containing:
+
+            :boot_device:
+                the boot device, one of :mod:`ironic.common.boot_devices` or
+                None if it is unknown.
+            :persistent:
+                Whether the boot device will persist to all future boots or
+                not, None if it is unknown.
+        """
+
+        with common.cimc_handle(task) as handle:
+            method = imcsdk.ImcCore.ExternalMethod("ConfigResolveClass")
+            method.Cookie = handle.cookie
+            method.InDn = "sys/rack-unit-1"
+            method.InHierarchical = "true"
+            method.ClassId = "lsbootDef"
+
+            try:
+                resp = handle.xml_query(method, imcsdk.WriteXmlOption.DIRTY)
+            except imcsdk.ImcException as e:
+                raise exception.CIMCException(node=task.node.uuid, error=e)
+            error = getattr(resp, 'error_code', None)
+            if error:
+                raise exception.CIMCException(node=task.node.uuid, error=error)
+
+            bootDevs = resp.OutConfigs.child[0].child
+
+            first_device = None
+            for dev in bootDevs:
+                try:
+                    if int(dev.Order) == 1:
+                        first_device = dev
+                        break
+                except (ValueError, AttributeError):
+                    pass
+
+            boot_device = (CIMC_TO_IRONIC_BOOT_DEVICE.get(
+                first_device.Rn) if first_device else None)
+
+            # Every boot device in CIMC is persistent right now
+            persistent = True if boot_device else None
+            return {'boot_device': boot_device, 'persistent': persistent}
+
+    def set_boot_device(self, task, device, persistent=True):
+        """Set the boot device for a node.
+
+        Set the boot device to use on next reboot of the node.
+
+        :param task: a task from TaskManager.
+        :param device: the boot device, one of
+                       :mod:`ironic.common.boot_devices`.
+        :param persistent: Every boot device in CIMC is persistent right now,
+                           so this value is ignored.
+        :raises: InvalidParameterValue if an invalid boot device is
+                 specified.
+        :raises: MissingParameterValue if a required parameter is missing
+        :raises: CIMCException if there is an error from CIMC
+        """
+
+        with common.cimc_handle(task) as handle:
+            dev = IRONIC_TO_CIMC_BOOT_DEVICE[device]
+
+            method = imcsdk.ImcCore.ExternalMethod("ConfigConfMo")
+            method.Cookie = handle.cookie
+            method.Dn = "sys/rack-unit-1/boot-policy"
+            method.InHierarchical = "true"
+
+            config = imcsdk.Imc.ConfigConfig()
+
+            bootMode = imcsdk.ImcCore.ManagedObject(dev[0])
+            bootMode.set_attr("access", dev[3])
+            bootMode.set_attr("type", dev[2])
+            bootMode.set_attr("Rn", dev[1])
+            bootMode.set_attr("order", "1")
+
+            config.add_child(bootMode)
+            method.InConfig = config
+
+            try:
+                resp = handle.xml_query(method, imcsdk.WriteXmlOption.DIRTY)
+            except imcsdk.ImcException as e:
+                raise exception.CIMCException(node=task.node.uuid, error=e)
+            error = getattr(resp, 'error_code')
+            if error:
+                raise exception.CIMCException(node=task.node.uuid, error=error)
+
+    def get_sensors_data(self, task):
+        raise NotImplementedError()
diff --git a/ironic/drivers/modules/cimc/power.py b/ironic/drivers/modules/cimc/power.py
new file mode 100644
index 0000000000..a6a40240a3
--- /dev/null
+++ b/ironic/drivers/modules/cimc/power.py
@@ -0,0 +1,184 @@
+# Copyright 2015, Cisco Systems.
+#
+# 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.
+
+from oslo_config import cfg
+from oslo_log import log as logging
+from oslo_service import loopingcall
+from oslo_utils import importutils
+
+from ironic.common import exception
+from ironic.common.i18n import _
+from ironic.common import states
+from ironic.conductor import task_manager
+from ironic.drivers import base
+from ironic.drivers.modules.cimc import common
+
+imcsdk = importutils.try_import('ImcSdk')
+
+opts = [
+    cfg.IntOpt('max_retry',
+               default=6,
+               help=_('Number of times a power operation needs to be '
+                      'retried')),
+    cfg.IntOpt('action_interval',
+               default=10,
+               help=_('Amount of time in seconds to wait in between power '
+                      'operations')),
+]
+
+CONF = cfg.CONF
+CONF.register_opts(opts, group='cimc')
+
+LOG = logging.getLogger(__name__)
+
+if imcsdk:
+    CIMC_TO_IRONIC_POWER_STATE = {
+        imcsdk.ComputeRackUnit.CONST_OPER_POWER_ON: states.POWER_ON,
+        imcsdk.ComputeRackUnit.CONST_OPER_POWER_OFF: states.POWER_OFF,
+    }
+
+    IRONIC_TO_CIMC_POWER_STATE = {
+        states.POWER_ON: imcsdk.ComputeRackUnit.CONST_ADMIN_POWER_UP,
+        states.POWER_OFF: imcsdk.ComputeRackUnit.CONST_ADMIN_POWER_DOWN,
+        states.REBOOT:
+            imcsdk.ComputeRackUnit.CONST_ADMIN_POWER_HARD_RESET_IMMEDIATE
+    }
+
+
+def _wait_for_state_change(target_state, task):
+    """Wait and check for the power state change
+
+    :param target_state: The target state we are waiting for.
+    :param task: a TaskManager instance containing the node to act on.
+    :raises: CIMCException if there is an error communicating with CIMC
+    """
+    store = {'state': None, 'retries': CONF.cimc.max_retry}
+
+    def _wait(store):
+
+        current_power_state = None
+        with common.cimc_handle(task) as handle:
+            try:
+                rack_unit = handle.get_imc_managedobject(
+                    None, None, params={"Dn": "sys/rack-unit-1"}
+                )
+            except imcsdk.ImcException as e:
+                raise exception.CIMCException(node=task.node.uuid, error=e)
+            else:
+                current_power_state = rack_unit[0].get_attr("OperPower")
+        store['state'] = CIMC_TO_IRONIC_POWER_STATE.get(current_power_state)
+
+        if store['state'] == target_state:
+            raise loopingcall.LoopingCallDone()
+
+        store['retries'] -= 1
+        if store['retries'] <= 0:
+            store['state'] = states.ERROR
+            raise loopingcall.LoopingCallDone()
+
+    timer = loopingcall.FixedIntervalLoopingCall(_wait, store)
+    timer.start(interval=CONF.cimc.action_interval).wait()
+    return store['state']
+
+
+class Power(base.PowerInterface):
+
+    def get_properties(self):
+        """Return the properties of the interface.
+
+        :returns: dictionary of <property name>:<property description> entries.
+        """
+        return common.COMMON_PROPERTIES
+
+    def validate(self, task):
+        """Check if node.driver_info contains the required CIMC credentials.
+
+        :param task: a TaskManager instance.
+        :raises: InvalidParameterValue if required CIMC credentials are
+                 missing.
+        """
+        common.parse_driver_info(task.node)
+
+    def get_power_state(self, task):
+        """Return the power state of the task's node.
+
+        :param task: a TaskManager instance containing the node to act on.
+        :raises: MissingParameterValue if a required parameter is missing.
+        :returns: a power state. One of :mod:`ironic.common.states`.
+        :raises: CIMCException if there is an error communicating with CIMC
+        """
+        current_power_state = None
+        with common.cimc_handle(task) as handle:
+            try:
+                rack_unit = handle.get_imc_managedobject(
+                    None, None, params={"Dn": "sys/rack-unit-1"}
+                )
+            except imcsdk.ImcException as e:
+                raise exception.CIMCException(node=task.node.uuid, error=e)
+            else:
+                current_power_state = rack_unit[0].get_attr("OperPower")
+        return CIMC_TO_IRONIC_POWER_STATE.get(current_power_state,
+                                              states.ERROR)
+
+    @task_manager.require_exclusive_lock
+    def set_power_state(self, task, pstate):
+        """Set the power state of the task's node.
+
+        :param task: a TaskManager instance containing the node to act on.
+        :param pstate: Any power state from :mod:`ironic.common.states`.
+        :raises: MissingParameterValue if a required parameter is missing.
+        :raises: InvalidParameterValue if an invalid power state is passed
+        :raises: CIMCException if there is an error communicating with CIMC
+        """
+        if pstate not in IRONIC_TO_CIMC_POWER_STATE:
+            msg = _("set_power_state called for %(node)s with "
+                    "invalid state %(state)s")
+            raise exception.InvalidParameterValue(
+                msg % {"node": task.node.uuid, "state": pstate})
+        with common.cimc_handle(task) as handle:
+            try:
+                handle.set_imc_managedobject(
+                    None, class_id="ComputeRackUnit",
+                    params={
+                        imcsdk.ComputeRackUnit.ADMIN_POWER:
+                            IRONIC_TO_CIMC_POWER_STATE[pstate],
+                        imcsdk.ComputeRackUnit.DN: "sys/rack-unit-1"
+                    })
+            except imcsdk.ImcException as e:
+                raise exception.CIMCException(node=task.node.uuid, error=e)
+
+        if pstate is states.REBOOT:
+            pstate = states.POWER_ON
+
+        state = _wait_for_state_change(pstate, task)
+        if state != pstate:
+            raise exception.PowerStateFailure(pstate=pstate)
+
+    @task_manager.require_exclusive_lock
+    def reboot(self, task):
+        """Perform a hard reboot of the task's node.
+
+        If the node is already powered on then it shall reboot the node, if
+        its off then the node will just be turned on.
+
+        :param task: a TaskManager instance containing the node to act on.
+        :raises: MissingParameterValue if a required parameter is missing.
+        :raises: CIMCException if there is an error communicating with CIMC
+        """
+        current_power_state = self.get_power_state(task)
+
+        if current_power_state == states.POWER_ON:
+            self.set_power_state(task, states.REBOOT)
+        elif current_power_state == states.POWER_OFF:
+            self.set_power_state(task, states.POWER_ON)
diff --git a/ironic/drivers/pxe.py b/ironic/drivers/pxe.py
index 5cfde84e9e..257be8cf12 100644
--- a/ironic/drivers/pxe.py
+++ b/ironic/drivers/pxe.py
@@ -25,6 +25,8 @@ from ironic.drivers import base
 from ironic.drivers.modules.amt import management as amt_management
 from ironic.drivers.modules.amt import power as amt_power
 from ironic.drivers.modules.amt import vendor as amt_vendor
+from ironic.drivers.modules.cimc import management as cimc_mgmt
+from ironic.drivers.modules.cimc import power as cimc_power
 from ironic.drivers.modules import iboot
 from ironic.drivers.modules.ilo import deploy as ilo_deploy
 from ironic.drivers.modules.ilo import inspect as ilo_inspect
@@ -340,6 +342,28 @@ class PXEAndUcsDriver(base.BaseDriver):
         self.vendor = iscsi_deploy.VendorPassthru()
 
 
+class PXEAndCIMCDriver(base.BaseDriver):
+    """PXE + Cisco IMC driver.
+
+    This driver implements the 'core' functionality, combining
+    :class:`ironic.drivers.modules.cimc.Power` for power on/off and reboot with
+    :class:`ironic.drivers.modules.pxe.PXEBoot` for booting the node and
+    :class:`ironic.drivers.modules.iscsi_deploy.ISCSIDeploy` for image
+    deployment. Implentations are in those respective classes; this
+    class is merely the glue between them.
+    """
+    def __init__(self):
+        if not importutils.try_import('ImcSdk'):
+            raise exception.DriverLoadError(
+                driver=self.__class__.__name__,
+                reason=_("Unable to import ImcSdk library"))
+        self.power = cimc_power.Power()
+        self.boot = pxe.PXEBoot()
+        self.deploy = iscsi_deploy.ISCSIDeploy()
+        self.management = cimc_mgmt.CIMCManagement()
+        self.vendor = iscsi_deploy.VendorPassthru()
+
+
 class PXEAndWakeOnLanDriver(base.BaseDriver):
     """PXE + WakeOnLan driver.
 
diff --git a/ironic/tests/db/utils.py b/ironic/tests/db/utils.py
index 39c7cb17d5..7ea0c58112 100644
--- a/ironic/tests/db/utils.py
+++ b/ironic/tests/db/utils.py
@@ -318,3 +318,11 @@ def get_test_ucs_info():
         "ucs_service_profile": "org-root/ls-devstack",
         "ucs_address": "ucs-b",
     }
+
+
+def get_test_cimc_info():
+    return {
+        "cimc_username": "admin",
+        "cimc_password": "password",
+        "cimc_address": "1.2.3.4",
+    }
diff --git a/ironic/tests/drivers/cimc/__init__.py b/ironic/tests/drivers/cimc/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/ironic/tests/drivers/cimc/test_common.py b/ironic/tests/drivers/cimc/test_common.py
new file mode 100644
index 0000000000..84478cd97f
--- /dev/null
+++ b/ironic/tests/drivers/cimc/test_common.py
@@ -0,0 +1,125 @@
+# Copyright 2015, Cisco Systems.
+#
+# 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.
+
+import mock
+
+from oslo_config import cfg
+from oslo_utils import importutils
+
+from ironic.common import exception
+from ironic.conductor import task_manager
+from ironic.drivers.modules.cimc import common as cimc_common
+from ironic.tests.conductor import utils as mgr_utils
+from ironic.tests.db import base as db_base
+from ironic.tests.db import utils as db_utils
+from ironic.tests.objects import utils as obj_utils
+
+imcsdk = importutils.try_import('ImcSdk')
+
+CONF = cfg.CONF
+
+
+class CIMCBaseTestCase(db_base.DbTestCase):
+
+    def setUp(self):
+        super(CIMCBaseTestCase, self).setUp()
+        mgr_utils.mock_the_extension_manager(driver="fake_cimc")
+        self.node = obj_utils.create_test_node(
+            self.context,
+            driver='fake_cimc',
+            driver_info=db_utils.get_test_cimc_info(),
+            instance_uuid="fake_uuid")
+        CONF.set_override('max_retry', 2, 'cimc')
+        CONF.set_override('action_interval', 0, 'cimc')
+
+
+class ParseDriverInfoTestCase(CIMCBaseTestCase):
+
+    def test_parse_driver_info(self):
+        info = cimc_common.parse_driver_info(self.node)
+
+        self.assertIsNotNone(info.get('cimc_address'))
+        self.assertIsNotNone(info.get('cimc_username'))
+        self.assertIsNotNone(info.get('cimc_password'))
+
+    def test_parse_driver_info_missing_address(self):
+        del self.node.driver_info['cimc_address']
+        self.assertRaises(exception.MissingParameterValue,
+                          cimc_common.parse_driver_info, self.node)
+
+    def test_parse_driver_info_missing_username(self):
+        del self.node.driver_info['cimc_username']
+        self.assertRaises(exception.MissingParameterValue,
+                          cimc_common.parse_driver_info, self.node)
+
+    def test_parse_driver_info_missing_password(self):
+        del self.node.driver_info['cimc_password']
+        self.assertRaises(exception.MissingParameterValue,
+                          cimc_common.parse_driver_info, self.node)
+
+
+@mock.patch.object(cimc_common, 'cimc_handle', autospec=True)
+class CIMCHandleLogin(CIMCBaseTestCase):
+
+    def test_cimc_handle_login(self, mock_handle):
+        info = cimc_common.parse_driver_info(self.node)
+
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                cimc_common.handle_login(task, handle, info)
+
+                handle.login.assert_called_once_with(
+                    self.node.driver_info['cimc_address'],
+                    self.node.driver_info['cimc_username'],
+                    self.node.driver_info['cimc_password'])
+
+    def test_cimc_handle_login_exception(self, mock_handle):
+        info = cimc_common.parse_driver_info(self.node)
+
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                handle.login.side_effect = imcsdk.ImcException('Boom')
+
+                self.assertRaises(exception.CIMCException,
+                                  cimc_common.handle_login,
+                                  task, handle, info)
+
+                handle.login.assert_called_once_with(
+                    self.node.driver_info['cimc_address'],
+                    self.node.driver_info['cimc_username'],
+                    self.node.driver_info['cimc_password'])
+
+
+class CIMCHandleTestCase(CIMCBaseTestCase):
+
+    @mock.patch.object(imcsdk, 'ImcHandle', autospec=True)
+    @mock.patch.object(cimc_common, 'handle_login', autospec=True)
+    def test_cimc_handle(self, mock_login, mock_handle):
+        mo_hand = mock.MagicMock()
+        mo_hand.username = self.node.driver_info.get('cimc_username')
+        mo_hand.password = self.node.driver_info.get('cimc_password')
+        mo_hand.name = self.node.driver_info.get('cimc_address')
+        mock_handle.return_value = mo_hand
+        info = cimc_common.parse_driver_info(self.node)
+
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with cimc_common.cimc_handle(task) as handle:
+                self.assertEqual(handle, mock_handle.return_value)
+
+        mock_login.assert_called_once_with(task, mock_handle.return_value,
+                                           info)
+        mock_handle.return_value.logout.assert_called_once_with()
diff --git a/ironic/tests/drivers/cimc/test_management.py b/ironic/tests/drivers/cimc/test_management.py
new file mode 100644
index 0000000000..dc3bf917af
--- /dev/null
+++ b/ironic/tests/drivers/cimc/test_management.py
@@ -0,0 +1,126 @@
+# Copyright 2015, Cisco Systems.
+#
+# 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.
+
+import mock
+
+from oslo_utils import importutils
+
+from ironic.common import boot_devices
+from ironic.common import exception
+from ironic.conductor import task_manager
+from ironic.drivers.modules.cimc import common
+from ironic.tests.drivers.cimc import test_common
+
+imcsdk = importutils.try_import('ImcSdk')
+
+
+@mock.patch.object(common, 'cimc_handle', autospec=True)
+class CIMCManagementTestCase(test_common.CIMCBaseTestCase):
+
+    def test_get_properties(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            self.assertEqual(common.COMMON_PROPERTIES,
+                             task.driver.management.get_properties())
+
+    @mock.patch.object(common, "parse_driver_info", autospec=True)
+    def test_validate(self, mock_driver_info, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            task.driver.management.validate(task)
+            mock_driver_info.assert_called_once_with(task.node)
+
+    def test_get_supported_boot_devices(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            expected = [boot_devices.PXE, boot_devices.DISK,
+                        boot_devices.CDROM]
+            result = task.driver.management.get_supported_boot_devices(task)
+            self.assertEqual(sorted(expected), sorted(result))
+
+    def test_get_boot_device(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                handle.xml_query.return_value.error_code = None
+                mock_dev = mock.MagicMock()
+                mock_dev.Order = 1
+                mock_dev.Rn = 'storage-read-write'
+                handle.xml_query().OutConfigs.child[0].child = [mock_dev]
+
+                device = task.driver.management.get_boot_device(task)
+                self.assertEqual(
+                    {'boot_device': boot_devices.DISK, 'persistent': True},
+                    device)
+
+    def test_get_boot_device_fail(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                handle.xml_query.return_value.error_code = None
+                mock_dev = mock.MagicMock()
+                mock_dev.Order = 1
+                mock_dev.Rn = 'storage-read-write'
+                handle.xml_query().OutConfigs.child[0].child = [mock_dev]
+
+                device = task.driver.management.get_boot_device(task)
+
+                self.assertEqual(
+                    {'boot_device': boot_devices.DISK, 'persistent': True},
+                    device)
+
+    def test_set_boot_device(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                handle.xml_query.return_value.error_code = None
+                task.driver.management.set_boot_device(task, boot_devices.DISK)
+                method = imcsdk.ImcCore.ExternalMethod("ConfigConfMo")
+                method.Cookie = handle.cookie
+                method.Dn = "sys/rack-unit-1/boot-policy"
+                method.InHierarchical = "true"
+
+                config = imcsdk.Imc.ConfigConfig()
+
+                bootMode = imcsdk.ImcCore.ManagedObject('lsbootStorage')
+                bootMode.set_attr("access", 'read-write')
+                bootMode.set_attr("type", 'storage')
+                bootMode.set_attr("Rn", 'storage-read-write')
+                bootMode.set_attr("order", "1")
+
+                config.add_child(bootMode)
+                method.InConfig = config
+
+                handle.xml_query.assert_called_once_with(
+                    method, imcsdk.WriteXmlOption.DIRTY)
+
+    def test_set_boot_device_fail(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                method = imcsdk.ImcCore.ExternalMethod("ConfigConfMo")
+                handle.xml_query.return_value.error_code = "404"
+
+                self.assertRaises(exception.CIMCException,
+                                  task.driver.management.set_boot_device,
+                                  task, boot_devices.DISK)
+
+                handle.xml_query.assert_called_once_with(
+                    method, imcsdk.WriteXmlOption.DIRTY)
+
+    def test_get_sensors_data(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            self.assertRaises(NotImplementedError,
+                              task.driver.management.get_sensors_data, task)
diff --git a/ironic/tests/drivers/cimc/test_power.py b/ironic/tests/drivers/cimc/test_power.py
new file mode 100644
index 0000000000..d82c71990c
--- /dev/null
+++ b/ironic/tests/drivers/cimc/test_power.py
@@ -0,0 +1,302 @@
+# Copyright 2015, Cisco Systems.
+#
+# 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.
+
+import mock
+
+from oslo_config import cfg
+from oslo_utils import importutils
+
+from ironic.common import exception
+from ironic.common import states
+from ironic.conductor import task_manager
+from ironic.drivers.modules.cimc import common
+from ironic.drivers.modules.cimc import power
+from ironic.tests.drivers.cimc import test_common
+
+imcsdk = importutils.try_import('ImcSdk')
+
+CONF = cfg.CONF
+
+
+@mock.patch.object(common, 'cimc_handle', autospec=True)
+class WaitForStateChangeTestCase(test_common.CIMCBaseTestCase):
+
+    def setUp(self):
+        super(WaitForStateChangeTestCase, self).setUp()
+        CONF.set_override('max_retry', 2, 'cimc')
+        CONF.set_override('action_interval', 0, 'cimc')
+
+    def test__wait_for_state_change(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                mock_rack_unit = mock.MagicMock()
+                mock_rack_unit.get_attr.return_value = (
+                    imcsdk.ComputeRackUnit.CONST_OPER_POWER_ON)
+
+                handle.get_imc_managedobject.return_value = [mock_rack_unit]
+
+                state = power._wait_for_state_change(states.POWER_ON, task)
+
+                handle.get_imc_managedobject.assert_called_once_with(
+                    None, None, params={"Dn": "sys/rack-unit-1"})
+
+                self.assertEqual(state, states.POWER_ON)
+
+    def test__wait_for_state_change_fail(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                mock_rack_unit = mock.MagicMock()
+                mock_rack_unit.get_attr.return_value = (
+                    imcsdk.ComputeRackUnit.CONST_OPER_POWER_OFF)
+
+                handle.get_imc_managedobject.return_value = [mock_rack_unit]
+
+                state = power._wait_for_state_change(states.POWER_ON, task)
+
+                calls = [
+                    mock.call(None, None, params={"Dn": "sys/rack-unit-1"}),
+                    mock.call(None, None, params={"Dn": "sys/rack-unit-1"})
+                ]
+                handle.get_imc_managedobject.assert_has_calls(calls)
+                self.assertEqual(state, states.ERROR)
+
+    def test__wait_for_state_change_imc_exception(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                handle.get_imc_managedobject.side_effect = (
+                    imcsdk.ImcException('Boom'))
+
+                self.assertRaises(
+                    exception.CIMCException,
+                    power._wait_for_state_change, states.POWER_ON, task)
+
+                handle.get_imc_managedobject.assert_called_once_with(
+                    None, None, params={"Dn": "sys/rack-unit-1"})
+
+
+@mock.patch.object(common, 'cimc_handle', autospec=True)
+class PowerTestCase(test_common.CIMCBaseTestCase):
+
+    def test_get_properties(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            self.assertEqual(common.COMMON_PROPERTIES,
+                             task.driver.power.get_properties())
+
+    @mock.patch.object(common, "parse_driver_info", autospec=True)
+    def test_validate(self, mock_driver_info, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            task.driver.power.validate(task)
+            mock_driver_info.assert_called_once_with(task.node)
+
+    def test_get_power_state(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                mock_rack_unit = mock.MagicMock()
+                mock_rack_unit.get_attr.return_value = (
+                    imcsdk.ComputeRackUnit.CONST_OPER_POWER_ON)
+
+                handle.get_imc_managedobject.return_value = [mock_rack_unit]
+
+                state = task.driver.power.get_power_state(task)
+
+                handle.get_imc_managedobject.assert_called_once_with(
+                    None, None, params={"Dn": "sys/rack-unit-1"})
+                self.assertEqual(states.POWER_ON, state)
+
+    def test_get_power_state_fail(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                mock_rack_unit = mock.MagicMock()
+                mock_rack_unit.get_attr.return_value = (
+                    imcsdk.ComputeRackUnit.CONST_OPER_POWER_ON)
+
+                handle.get_imc_managedobject.side_effect = (
+                    imcsdk.ImcException("boom"))
+
+                self.assertRaises(exception.CIMCException,
+                                  task.driver.power.get_power_state, task)
+
+                handle.get_imc_managedobject.assert_called_once_with(
+                    None, None, params={"Dn": "sys/rack-unit-1"})
+
+    def test_set_power_state_invalid_state(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            self.assertRaises(exception.InvalidParameterValue,
+                              task.driver.power.set_power_state,
+                              task, states.ERROR)
+
+    def test_set_power_state_reboot_ok(self, mock_handle):
+        hri = imcsdk.ComputeRackUnit.CONST_ADMIN_POWER_HARD_RESET_IMMEDIATE
+
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                mock_rack_unit = mock.MagicMock()
+                mock_rack_unit.get_attr.side_effect = [
+                    imcsdk.ComputeRackUnit.CONST_OPER_POWER_OFF,
+                    imcsdk.ComputeRackUnit.CONST_OPER_POWER_ON
+                ]
+                handle.get_imc_managedobject.return_value = [mock_rack_unit]
+
+                task.driver.power.set_power_state(task, states.REBOOT)
+
+                handle.set_imc_managedobject.assert_called_once_with(
+                    None, class_id="ComputeRackUnit",
+                    params={
+                        imcsdk.ComputeRackUnit.ADMIN_POWER: hri,
+                        imcsdk.ComputeRackUnit.DN: "sys/rack-unit-1"
+                    })
+
+                handle.get_imc_managedobject.assert_called_with(
+                    None, None, params={"Dn": "sys/rack-unit-1"})
+
+    def test_set_power_state_reboot_fail(self, mock_handle):
+        hri = imcsdk.ComputeRackUnit.CONST_ADMIN_POWER_HARD_RESET_IMMEDIATE
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                handle.get_imc_managedobject.side_effect = (
+                    imcsdk.ImcException("boom"))
+
+                self.assertRaises(exception.CIMCException,
+                                  task.driver.power.set_power_state,
+                                  task, states.REBOOT)
+
+                handle.set_imc_managedobject.assert_called_once_with(
+                    None, class_id="ComputeRackUnit",
+                    params={
+                        imcsdk.ComputeRackUnit.ADMIN_POWER: hri,
+                        imcsdk.ComputeRackUnit.DN: "sys/rack-unit-1"
+                    })
+
+                handle.get_imc_managedobject.assert_called_with(
+                    None, None, params={"Dn": "sys/rack-unit-1"})
+
+    def test_set_power_state_on_ok(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                mock_rack_unit = mock.MagicMock()
+                mock_rack_unit.get_attr.side_effect = [
+                    imcsdk.ComputeRackUnit.CONST_OPER_POWER_OFF,
+                    imcsdk.ComputeRackUnit.CONST_OPER_POWER_ON
+                ]
+                handle.get_imc_managedobject.return_value = [mock_rack_unit]
+
+                task.driver.power.set_power_state(task, states.POWER_ON)
+
+                handle.set_imc_managedobject.assert_called_once_with(
+                    None, class_id="ComputeRackUnit",
+                    params={
+                        imcsdk.ComputeRackUnit.ADMIN_POWER:
+                            imcsdk.ComputeRackUnit.CONST_ADMIN_POWER_UP,
+                        imcsdk.ComputeRackUnit.DN: "sys/rack-unit-1"
+                    })
+
+                handle.get_imc_managedobject.assert_called_with(
+                    None, None, params={"Dn": "sys/rack-unit-1"})
+
+    def test_set_power_state_on_fail(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                handle.get_imc_managedobject.side_effect = (
+                    imcsdk.ImcException("boom"))
+
+                self.assertRaises(exception.CIMCException,
+                                  task.driver.power.set_power_state,
+                                  task, states.POWER_ON)
+
+                handle.set_imc_managedobject.assert_called_once_with(
+                    None, class_id="ComputeRackUnit",
+                    params={
+                        imcsdk.ComputeRackUnit.ADMIN_POWER:
+                            imcsdk.ComputeRackUnit.CONST_ADMIN_POWER_UP,
+                        imcsdk.ComputeRackUnit.DN: "sys/rack-unit-1"
+                    })
+
+                handle.get_imc_managedobject.assert_called_with(
+                    None, None, params={"Dn": "sys/rack-unit-1"})
+
+    def test_set_power_state_off_ok(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                mock_rack_unit = mock.MagicMock()
+                mock_rack_unit.get_attr.side_effect = [
+                    imcsdk.ComputeRackUnit.CONST_OPER_POWER_ON,
+                    imcsdk.ComputeRackUnit.CONST_OPER_POWER_OFF
+                ]
+                handle.get_imc_managedobject.return_value = [mock_rack_unit]
+
+                task.driver.power.set_power_state(task, states.POWER_OFF)
+
+                handle.set_imc_managedobject.assert_called_once_with(
+                    None, class_id="ComputeRackUnit",
+                    params={
+                        imcsdk.ComputeRackUnit.ADMIN_POWER:
+                            imcsdk.ComputeRackUnit.CONST_ADMIN_POWER_DOWN,
+                        imcsdk.ComputeRackUnit.DN: "sys/rack-unit-1"
+                    })
+
+                handle.get_imc_managedobject.assert_called_with(
+                    None, None, params={"Dn": "sys/rack-unit-1"})
+
+    def test_set_power_state_off_fail(self, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            with mock_handle(task) as handle:
+                handle.get_imc_managedobject.side_effect = (
+                    imcsdk.ImcException("boom"))
+
+                self.assertRaises(exception.CIMCException,
+                                  task.driver.power.set_power_state,
+                                  task, states.POWER_OFF)
+
+                handle.set_imc_managedobject.assert_called_once_with(
+                    None, class_id="ComputeRackUnit",
+                    params={
+                        imcsdk.ComputeRackUnit.ADMIN_POWER:
+                            imcsdk.ComputeRackUnit.CONST_ADMIN_POWER_DOWN,
+                        imcsdk.ComputeRackUnit.DN: "sys/rack-unit-1"
+                    })
+
+                handle.get_imc_managedobject.assert_called_with(
+                    None, None, params={"Dn": "sys/rack-unit-1"})
+
+    @mock.patch.object(power.Power, "set_power_state", autospec=True)
+    @mock.patch.object(power.Power, "get_power_state", autospec=True)
+    def test_reboot_on(self, mock_get_state, mock_set_state, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            mock_get_state.return_value = states.POWER_ON
+            task.driver.power.reboot(task)
+            mock_set_state.assert_called_with(mock.ANY, task, states.REBOOT)
+
+    @mock.patch.object(power.Power, "set_power_state", autospec=True)
+    @mock.patch.object(power.Power, "get_power_state", autospec=True)
+    def test_reboot_off(self, mock_get_state, mock_set_state, mock_handle):
+        with task_manager.acquire(self.context, self.node.uuid,
+                                  shared=False) as task:
+            mock_get_state.return_value = states.POWER_OFF
+            task.driver.power.reboot(task)
+            mock_set_state.assert_called_with(mock.ANY, task, states.POWER_ON)
diff --git a/ironic/tests/drivers/third_party_driver_mocks.py b/ironic/tests/drivers/third_party_driver_mocks.py
index 909ea0a0e8..6389c63828 100644
--- a/ironic/tests/drivers/third_party_driver_mocks.py
+++ b/ironic/tests/drivers/third_party_driver_mocks.py
@@ -232,3 +232,12 @@ if not ucssdk:
     if 'ironic.drivers.modules.ucs' in sys.modules:
         six.moves.reload_module(
             sys.modules['ironic.drivers.modules.ucs'])
+
+imcsdk = importutils.try_import('ImcSdk')
+if not imcsdk:
+    imcsdk = mock.MagicMock()
+    imcsdk.ImcException = Exception
+    sys.modules['ImcSdk'] = imcsdk
+    if 'ironic.drivers.modules.cimc' in sys.modules:
+        six.moves.reload_module(
+            sys.modules['ironic.drivers.modules.cimc'])
diff --git a/setup.cfg b/setup.cfg
index e8483a00c4..45c08aac1d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -56,6 +56,7 @@ ironic.drivers =
     fake_amt = ironic.drivers.fake:FakeAMTDriver
     fake_msftocs = ironic.drivers.fake:FakeMSFTOCSDriver
     fake_ucs = ironic.drivers.fake:FakeUcsDriver
+    fake_cimc = ironic.drivers.fake:FakeCIMCDriver
     fake_wol = ironic.drivers.fake:FakeWakeOnLanDriver
     iscsi_ilo = ironic.drivers.ilo:IloVirtualMediaIscsiDriver
     iscsi_irmc = ironic.drivers.irmc:IRMCVirtualMediaIscsiDriver
@@ -73,6 +74,8 @@ ironic.drivers =
     pxe_msftocs = ironic.drivers.pxe:PXEAndMSFTOCSDriver
     pxe_ucs = ironic.drivers.pxe:PXEAndUcsDriver
     pxe_wol = ironic.drivers.pxe:PXEAndWakeOnLanDriver
+    pxe_iscsi_cimc = ironic.drivers.pxe:PXEAndCIMCDriver
+    pxe_agent_cimc = ironic.drivers.agent:AgentAndCIMCDriver
 
 ironic.database.migration_backend =
     sqlalchemy = ironic.db.sqlalchemy.migration