Esempio n. 1
0
class HardwareProfileDbApi(TortugaDbApi):
    """
    HardwareProfile DB API class.
    """
    def __init__(self):
        TortugaDbApi.__init__(self)

        self._hardwareProfilesDbHandler = HardwareProfilesDbHandler()
        self._nodesDbHandler = NodesDbHandler()
        self._globalParametersDbHandler = GlobalParametersDbHandler()
        self._adminsDbHandler = AdminsDbHandler()
        self._nicsDbHandler = NicsDbHandler()
        self._resourceAdaptersDbHandler = ResourceAdaptersDbHandler()
        self._networkDevicesDbHandler = NetworkDevicesDbHandler()
        self._networksDbHandler = NetworksDbHandler()

    def getHardwareProfile(self,
                           name: str,
                           optionDict: Optional[Union[dict, None]] = None):
        """
        Get hardwareProfile from the db.

            Returns:
                hardwareProfile
            Throws:
                HardwareProfileNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            dbHardwareProfile = self._hardwareProfilesDbHandler.\
                getHardwareProfile(session, name)

            self.loadRelations(dbHardwareProfile, optionDict)
            self.loadRelations(dbHardwareProfile, dict(tags=True))

            return HardwareProfile.getFromDbDict(dbHardwareProfile.__dict__)
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def getHardwareProfileById(self,
                               hardwareProfileId: int,
                               optionDict: Optional[Union[dict, None]] = None):
        """
        Get hardwareProfile from the db.

            Returns:
                hardwareProfile
            Throws:
                HardwareProfileNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            dbHardwareProfile = self._hardwareProfilesDbHandler.\
                getHardwareProfileById(session, hardwareProfileId)

            self.loadRelations(dbHardwareProfile, optionDict)

            return HardwareProfile.getFromDbDict(dbHardwareProfile.__dict__)
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def getHardwareProfileList(self,
                               optionDict: Optional[Union[dict, None]] = None,
                               tags: Optional[Union[dict, None]] = None):
        """
        Get list of all available hardwareProfiles from the db.

            Returns:
                [hardwareProfile]
            Throws:
                DbError
        """

        session = DbManager().openSession()

        try:
            dbHardwareProfileList = self._hardwareProfilesDbHandler.\
                getHardwareProfileList(session, tags=tags)

            hardwareProfileList = TortugaObjectList()

            for dbHardwareProfile in dbHardwareProfileList:
                # For now expand networks
                self.loadRelation(dbHardwareProfile, 'hardwareprofilenetworks')

                self.loadRelations(dbHardwareProfile, optionDict)

                self.loadRelations(dbHardwareProfile, dict(tags=True))

                hardwareProfileList.append(
                    HardwareProfile.getFromDbDict(dbHardwareProfile.__dict__))

            return hardwareProfileList
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def setIdleSoftwareProfile(self,
                               hardwareProfileName,
                               softwareProfileName=None):
        """
        Sets the idle software profile

        Returns:
            -none-

        Raises:
            SoftwareProfileNotFound
            SoftwareProfileNotIdle
        """

        session = DbManager().openSession()

        try:
            dbSoftwareProfile = SoftwareProfilesDbHandler().\
                getSoftwareProfile(session, softwareProfileName) \
                if softwareProfileName else None

            dbHardwareProfile = self._hardwareProfilesDbHandler.\
                getHardwareProfile(session, hardwareProfileName)

            self._hardwareProfilesDbHandler.setIdleSoftwareProfile(
                dbHardwareProfile, dbSoftwareProfile)

            session.commit()
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def addHardwareProfile(self, hardwareProfile, session=None):
        """
        Insert hardwareProfile into the db.

            Returns:
                (none)
            Throws:
                HardwareProfileAlreadyExists
                DbError
        """

        # Keep local 'session' instance.  If 'session' is None,
        # ensure transaction is committed before returning to the
        # caller, otherwise the caller is responsible.  On exceptions,
        # the rollback is performed regardless.

        _session = session

        if _session is None:
            _session = DbManager().openSession()

        try:
            try:
                self._hardwareProfilesDbHandler.getHardwareProfile(
                    _session, hardwareProfile.getName())

                raise HardwareProfileAlreadyExists(
                    'Hardware profile [%s] already exists' % (hardwareProfile))
            except HardwareProfileNotFound as ex:
                pass

            dbHardwareProfile = self.__populateHardwareProfile(
                _session, hardwareProfile)

            _session.add(dbHardwareProfile)

            if session is None:
                _session.commit()

            self.getLogger().info('Added hardware profile [%s]' %
                                  (dbHardwareProfile.name))
        except TortugaException as ex:
            _session.rollback()
            raise
        except Exception as ex:
            _session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            if session is None:
                DbManager().closeSession()

    def deleteHardwareProfile(self, name):
        """
        Delete hardwareProfile from the db.

            Returns:
                None
            Throws:
                HardwareProfileNotFound
                DbError
                TortugaException
        """

        session = DbManager().openSession()

        try:
            hwProfile = self._hardwareProfilesDbHandler.getHardwareProfile(
                session, name)

            if hwProfile.nodes:
                raise TortugaException(
                    'Unable to remove hardware profile with associated'
                    ' nodes')

            # First delete the mappings
            hwProfile.mappedsoftwareprofiles = []

            self.getLogger().debug(
                'Marking hardware profile [%s] for deletion' % (name))

            session.delete(hwProfile)

            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def copyHardwareProfile(self, srcHardwareProfileName,
                            dstHardwareProfileName):
        session = DbManager().openSession()

        try:
            srcHardwareProfile = self.getHardwareProfile(
                srcHardwareProfileName, {
                    'admins': True,
                    'hardwareprofilenetworks': True,
                    'nics': True,
                    'resourceadapter': True,
                })

            dstHardwareProfile = \
                self.getHardwareProfile(srcHardwareProfileName)

            dstHardwareProfile.setName(dstHardwareProfileName)

            newDescription = 'Copy of %s' % (
                dstHardwareProfile.getDescription())

            dstHardwareProfile.setDescription(newDescription)

            dstHardwareProfile.setNetworks(srcHardwareProfile.getNetworks())

            dstHardwareProfile.setProvisioningNics(
                srcHardwareProfile.getProvisioningNics())

            dstHardwareProfile.setResourceAdapter(
                srcHardwareProfile.getResourceAdapter())

            self.addHardwareProfile(dstHardwareProfile, session)

            session.commit()
        finally:
            DbManager().closeSession()

    def getNode(self, name):
        """
        Get node from the db.

            Returns:
                node
            Throws:
                NodeNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            return self._nodesDbHandler.getNode(session, name)
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def deleteNode(self, name):
        """
        Delete node from the db.

            Returns:
                None
            Throws:
                NodeNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            self._nodesDbHandler.deleteNode(session, name)

            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def addAdmin(self, hardwareProfileName, adminUsername):
        """
        Add an admin to this hardware profile

        Raises:
            AdminAlreadyExists
        """

        session = DbManager().openSession()

        try:
            dbAdmin = self._adminsDbHandler.getAdmin(session, adminUsername)

            dbHardwareProfile = self._hardwareProfilesDbHandler.\
                getHardwareProfile(session, hardwareProfileName)

            if dbAdmin in dbHardwareProfile.admins:
                raise AdminAlreadyExists(
                    'The admin %s is already associated with %s.' %
                    (adminUsername, hardwareProfileName))

            dbHardwareProfile.admins.append(dbAdmin)

            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def deleteAdmin(self, hardwareProfileName, adminUsername):
        """
        Delete an admin from a hardware profile

        Raises:
            AdminNotFound
        """

        session = DbManager().openSession()

        try:
            dbAdmin = self._adminsDbHandler.getAdmin(session, adminUsername)

            dbHardwareProfile = self._hardwareProfilesDbHandler.\
                getHardwareProfile(session, hardwareProfileName)

            if dbAdmin not in dbHardwareProfile.admins:
                raise AdminNotFound('Admin user [%s] not associated with %s.' %
                                    (adminUsername, hardwareProfileName))

            dbHardwareProfile.admins.remove(dbAdmin)

            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def updateHardwareProfile(self, hardwareProfileObject):
        """ Update Hardware Profile Object """

        session = DbManager().openSession()

        try:
            dbHardwareProfile = self._hardwareProfilesDbHandler.\
                getHardwareProfileById(session,
                                       hardwareProfileObject.getId())

            self.__populateHardwareProfile(session, hardwareProfileObject,
                                           dbHardwareProfile)

            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def __getInstallerNode(self, session):
        return self._nodesDbHandler.getNode(session,
                                            ConfigManager().getInstaller())

    def __get_provisioning_nics(self, session):
        return self.__getInstallerNode(session).nics

    def __get_all_networks(self, session):
        return self._networksDbHandler.getNetworkList(session)

    def __get_network_devices(self, session):         \
            # pylint: disable=no-self-use

        return session.query(NetworkDevices).all()

    def __populateHardwareProfile(self,
                                  session,
                                  hardwareProfile,
                                  dbHardwareProfile=None):
        """
        Helper function for creating / updating HardwareProfiles. If
        'dbHardwareProfile' is specified, this is an update (vs. add) operation

        Raises:
            NicNotFound
        """

        # Preload provisioning nics and networks
        prov_nics = self.__get_provisioning_nics(session)
        all_networks = self.__get_all_networks(session)

        networkdevices = self.__get_network_devices(session)

        # Validate hw profile
        if hardwareProfile.getName() is None:
            raise ConfigurationError('Hardware profile requires name.')

        if hardwareProfile.getNameFormat() is None:
            raise ConfigurationError(
                'Hardware profile requires name format field.')

        # Handle the special case of a hardware profile not having an
        # associated idle software profile (ie. Installer hardware
        # profile)
        idleSoftwareProfileId = hardwareProfile.getIdleSoftwareProfileId() \
            if hardwareProfile.getIdleSoftwareProfileId else None

        if dbHardwareProfile is None:
            dbHardwareProfile = HardwareProfiles()

        dbHardwareProfile.name = hardwareProfile.getName()
        dbHardwareProfile.description = hardwareProfile.getDescription()
        dbHardwareProfile.nameFormat = hardwareProfile.getNameFormat()

        if hardwareProfile.getInstallType() is None:
            if hardwareProfile.getLocation() == 'remote':
                dbHardwareProfile.installType = 'bootstrap'
            else:
                raise ConfigurationError(
                    'Hardware profile must have valid install type.')
        else:
            dbHardwareProfile.installType = hardwareProfile.\
                getInstallType()

        if hardwareProfile.getLocation() != 'remote':
            dbHardwareProfile.kernel = hardwareProfile.getKernel()
            dbHardwareProfile.kernelParams = hardwareProfile.\
                getKernelParams()
            dbHardwareProfile.initrd = hardwareProfile.getInitrd()
            dbHardwareProfile.localBootParams = hardwareProfile.\
                getLocalBootParams()

        dbHardwareProfile.softwareOverrideAllowed = hardwareProfile.\
            getSoftwareOverrideAllowed()
        dbHardwareProfile.idleSoftwareProfileId = idleSoftwareProfileId
        dbHardwareProfile.location = hardwareProfile.getLocation()

        dbHardwareProfile.hypervisorSoftwareProfileId = hardwareProfile.\
            getHypervisorSoftwareProfileId()
        dbHardwareProfile.maxUnits = hardwareProfile.getMaxUnits()
        dbHardwareProfile.bcastEnabled = hardwareProfile.getBcastEnabled()
        dbHardwareProfile.mcastEnabled = hardwareProfile.getMcastEnabled()
        dbHardwareProfile.cost = hardwareProfile.getCost()

        # Add resource adapter
        resourceAdapter = hardwareProfile.getResourceAdapter()
        if resourceAdapter:
            dbHardwareProfile.resourceAdapter = self.\
                _resourceAdaptersDbHandler.getResourceAdapter(
                    session, resourceAdapter.getName())

            dbHardwareProfile.resourceAdapterId = dbHardwareProfile.\
                resourceAdapter.id

        # Add networks
        networks = []
        for network in hardwareProfile.getNetworks():
            for prov_network in all_networks:
                if prov_network.address == network.getAddress():
                    dbNetwork = prov_network

                    break
            else:
                raise NetworkNotFound('Network [%s] does not exist' %
                                      (network.getAddress()))

            # Lookup network device
            for network_device in networkdevices:
                if network.getNetworkDevice().getName() == network_device.name:
                    dbNetworkDevice = network_device

                    break
            else:
                dbNetworkDevice = NetworkDevices()
                dbNetworkDevice.name = network.getNetworkDevice().getName()

            # Now check if we have this one already...
            for dbHardwareProfileNetwork in dbHardwareProfile.\
                    hardwareprofilenetworks:
                if dbHardwareProfileNetwork.\
                    networkDeviceId == dbNetworkDevice.id and \
                        dbHardwareProfileNetwork.networkId == dbNetwork.id:
                    break
            else:
                dbHardwareProfileNetwork = HardwareProfileNetworks()

                if dbNetwork.id is not None:
                    dbHardwareProfileNetwork.networkId = dbNetwork.id
                else:
                    dbHardwareProfileNetwork.network = dbNetwork

                dbHardwareProfileNetwork.hardwareProfileId = \
                    dbHardwareProfile.id

                if dbNetworkDevice.id is not None:
                    dbHardwareProfileNetwork.networkDeviceId = \
                        dbNetworkDevice.id
                else:
                    dbHardwareProfileNetwork.networkdevice = dbNetworkDevice

                dbHardwareProfile.hardwareprofilenetworks.append(
                    dbHardwareProfileNetwork)

            networks.append(dbHardwareProfileNetwork)

        # Now remove all old networks
        for dbNetwork in dbHardwareProfile.hardwareprofilenetworks:
            for network in networks:
                if network.networkDeviceId == dbNetwork.networkDeviceId \
                        and network.networkId == dbNetwork.networkId:
                    # Its a keeper
                    break
            else:
                # No match...delete time
                session.delete(dbNetwork)

        # Add provisioning Nics
        if hardwareProfile.getProvisioningNics():
            # Only one provisioning nic is possible
            nic = hardwareProfile.getProvisioningNics()[0]

            for prov_nic in prov_nics:
                if nic.getIp() == prov_nic.ip:
                    dbNic = prov_nic

                    break
            else:
                raise NicNotFound('Provisioning NIC with IP [%s] not found' %
                                  (nic.getIp()))

            if dbNic not in dbHardwareProfile.nics:
                dbHardwareProfile.nics.append(dbNic)

        return dbHardwareProfile

    def getHypervisorNodes(self, hardwareProfileName):
        """
        Get list of nodes that belong to hypervisorSoftwareProfileId
        assigned to the given hardware profile name.
        """

        session = DbManager().openSession()

        try:
            dbHardwareProfile = self._hardwareProfilesDbHandler.\
                getHardwareProfile(session, hardwareProfileName)

            if not dbHardwareProfile.hypervisor:
                return TortugaObjectList()

            return self.getTortugaObjectList(
                Node, dbHardwareProfile.hypervisor.nodes)
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def setProvisioningNic(self, hardwareProfileName, nicId):
        session = DbManager().openSession()

        try:
            dbNic = self._nicsDbHandler.getNicById(session, nicId)

            dbHardwareProfile = self._hardwareProfilesDbHandler.\
                getHardwareProfile(session, hardwareProfileName)

            dbHardwareProfile.nics.append(dbNic)

            session.commit()
        except sqlalchemy.exc.IntegrityError as ex:
            # Entry for this hwProfile/nicId already exists, ignore
            self.getLogger().debug(
                'setProvisioningNic(): entry already exists for'
                ' hwProfile=%s, nicId=%d' % (hardwareProfileName, nicId))
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def getProvisioningNicForNetwork(self, network, netmask):
        """
        Raises:
            NicNotFound
        """

        session = DbManager().openSession()

        try:
            installer_node = self.__getInstallerNode(session)

            nics = [
                dbNic for dbNic in installer_node.hardwareprofile.nics
                if dbNic.network.address == network
                and dbNic.network.netmask == netmask
            ]

            if not nics:
                raise NicNotFound(
                    'Unable to find provisioning NIC for network [%s]'
                    ' netmask [%s]' % (network, netmask))

            return tortuga.objects.nic.Nic.getFromDbDict(nics[0].__dict__)
        except TortugaException as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()
Esempio n. 2
0
class SoftwareProfileDbApi(TortugaDbApi):
    """
    SoftwareProfile DB API class.
    """
    def __init__(self):
        TortugaDbApi.__init__(self)

        self._softwareProfilesDbHandler = SoftwareProfilesDbHandler()
        self._nodesDbHandler = NodesDbHandler()
        self._globalParametersDbHandler = GlobalParametersDbHandler()
        self._adminsDbHandler = AdminsDbHandler()
        self._osDbHandler = OperatingSystemsDbHandler()

    def getSoftwareProfile(self, name, optionDict=None):
        """
        Get softwareProfile from the db.

            Returns:
               softwareProfile
            Throws:
                SoftwareProfileNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            dbSoftwareProfile = self._softwareProfilesDbHandler.\
                getSoftwareProfile(session, name)

            self.loadRelations(dbSoftwareProfile, optionDict or {})

            self.loadRelations(dbSoftwareProfile, dict(tags=True))

            return SoftwareProfile.getFromDbDict(dbSoftwareProfile.__dict__)
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def getSoftwareProfileById(self, softwareProfileId, optionDict=None):
        """
        Get softwareProfile from the db.

            Returns:
               softwareProfile
            Throws:
                SoftwareProfileNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            dbSoftwareProfile = self._softwareProfilesDbHandler.\
                getSoftwareProfileById(session, softwareProfileId)

            self.loadRelations(dbSoftwareProfile, optionDict or {})

            return SoftwareProfile.getFromDbDict(dbSoftwareProfile.__dict__)
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def getSoftwareProfileList(self, tags=None):
        """
        Get list of all available softwareProfiles from the db.

            Returns:
                [softwareProfile]
            Throws:
                DbError
        """

        session = DbManager().openSession()

        try:
            dbSoftwareProfileList = self._softwareProfilesDbHandler.\
                getSoftwareProfileList(session, tags=tags)

            softwareProfileList = TortugaObjectList()

            for dbSoftwareProfile in dbSoftwareProfileList:
                self.loadRelations(
                    dbSoftwareProfile, {
                        'components': True,
                        'partitions': True,
                        'hardwareprofiles': True,
                        'tags': True,
                    })

                softwareProfileList.append(
                    SoftwareProfile.getFromDbDict(dbSoftwareProfile.__dict__))

            return softwareProfileList
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def getIdleSoftwareProfileList(self):
        """
        Get list of all available idle softwareProfiles from the db.

            Returns:
                [idle softwareProfile]
            Throws:
                DbError
        """

        session = DbManager().openSession()

        try:
            dbSoftwareProfileList = self._softwareProfilesDbHandler.\
                getIdleSoftwareProfileList(session)

            return self.getTortugaObjectList(SoftwareProfile,
                                             dbSoftwareProfileList)
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def setIdleState(self, softwareProfileName, state):
        """
        Set idle state of a software profile

            Returns:
                -none-
            Throws:
                DbError
        """

        session = DbManager().openSession()

        try:
            dbSoftwareProfile = self._softwareProfilesDbHandler.\
                getSoftwareProfile(session, softwareProfileName)

            self.getLogger().debug(
                'Setting idle state [%s] on software profile [%s]' %
                (state, dbSoftwareProfile.name))

            dbSoftwareProfile.isIdle = state

            session.commit()
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def addSoftwareProfile(self, softwareProfile, session=None):
        """
        Insert software profile into the db.

        Raises:
            SoftwareProfileAlreadyExists
            DbError
        """

        # Keep local 'session' instance.  If 'session' is None,
        # ensure transaction is committed before returning to the
        # caller, otherwise the caller is responsible.  On exceptions,
        # the rollback is performed regardless.
        _session = session

        if _session is None:
            _session = DbManager().openSession()

        try:
            try:
                dbSoftwareProfile = self._softwareProfilesDbHandler.\
                    getSoftwareProfile(
                        _session, softwareProfile.getName())

                raise SoftwareProfileAlreadyExists(
                    'Software profile [%s] already exists' % (softwareProfile))
            except SoftwareProfileNotFound as ex:
                pass

            dbSoftwareProfile = self.__populateSoftwareProfile(
                _session, softwareProfile)

            _session.query(func.max(SoftwareProfiles.id)).one()

            softwareProfile.setId(dbSoftwareProfile.id)

            if session is None:
                _session.commit()

            self.getLogger().info('Added software profile [%s]' %
                                  (dbSoftwareProfile.name))

            return dbSoftwareProfile
        except TortugaException as ex:
            _session.rollback()
            raise
        except Exception as ex:
            _session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            if session is None:
                DbManager().closeSession()

    def deleteSoftwareProfile(self, name):
        """
        Delete softwareProfile from the db.

            Returns:
                None
            Throws:
                SoftwareProfileNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            # This role of this lookup is twofold.  One, it validates
            # the existence of the software profile to be deleted and
            # second, gets the id for looking up any associated nodes
            dbSwProfile = self._softwareProfilesDbHandler.\
                getSoftwareProfile(session, name)

            if dbSwProfile.nodes:
                # The software profile cannot be removed while
                # associated
                # nodes exist
                raise TortugaException('Unable to delete software profile with'
                                       ' associated nodes')

            if dbSwProfile.isIdle:
                # Ensure this software profile is not associated with
                # a hardware profile

                if dbSwProfile.hwprofileswithidle:
                    # This software profile is associated with one
                    # or more hardware profiles
                    raise TortugaException(
                        'Unable to delete software profile.'
                        '  This software profile is associated'
                        ' with one or more hardware profiles: [%s]' %
                        (' '.join([
                            hwprofile.name
                            for hwprofile in dbSwProfile.hwprofileswithidle
                        ])))

            # Proceed with software profile deletion
            self.getLogger().debug(
                'Marking software profile [%s] for deletion' % (name))

            session.delete(dbSwProfile)

            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def getNode(self, name):
        """
        Get node from the db.

            Returns:
                node
            Throws:
                NodeNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            return self._nodesDbHandler.getNode(session, name)
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def getAllEnabledComponentList(self):
        """
        Get a list of all enabled components in the system

            Returns:
                [ components ]
            Throws:
                DbError
        """

        session = DbManager().openSession()

        try:
            handler = ComponentsDbHandler()

            dbComponents = handler.getEnabledComponentList(session)

            return self.getTortugaObjectList(Component, dbComponents)
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def getEnabledComponentList(self, name):
        """
        Get a list of enabled components from the db.

            Returns:
                node
            Throws:
                DbError
        """

        session = DbManager().openSession()

        try:
            componentList = TortugaObjectList()

            for c in self._softwareProfilesDbHandler.getSoftwareProfile(
                    session, name).components:
                self.loadRelation(c, 'kit')

                componentList.append(Component.getFromDbDict(c.__dict__))

            return componentList
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def getNodeList(self, softwareProfile):
        """
        Get list of nodes in 'softwareProfile'

            Returns:
                [node]
            Throws:
                DbError
        """

        session = DbManager().openSession()

        try:
            dbSoftwareProfile = self._softwareProfilesDbHandler.\
                getSoftwareProfile(session, softwareProfile)

            nodeList = TortugaObjectList()

            for dbNode in dbSoftwareProfile.nodes:
                self.loadRelation(dbNode, 'hardwareprofile')

                nodeList.append(Node.getFromDbDict(dbNode.__dict__))

            return nodeList
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def getNodeListByNodeStateAndSoftwareProfileName(self, nodeState,
                                                     softwareProfileName):
        """
        Return a list of Node objects based on the specified node state
        and software profile
        """

        session = DbManager().openSession()

        try:
            dbNodes = self._nodesDbHandler.\
                getNodeListByNodeStateAndSoftwareProfileName(
                    session, nodeState, softwareProfileName)

            return self.getTortugaObjectList(Node, dbNodes)
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def deleteNode(self, name):
        """
        Delete node from the db.

            Returns:
                None
            Throws:
                NodeNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            self._nodesDbHandler.deleteNode(session, name)
            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def getPackageList(self, softwareProfileName):
        """
        Get a list of packages from the db.

            Returns:
                [package]
            Throws:
                SoftwareProfileNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            dbPackages = self._softwareProfilesDbHandler.\
                getSoftwareProfile(
                    session, softwareProfileName).packages

            return self.getTortugaObjectList(Package, dbPackages)
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def getPartitionList(self, softwareProfileName):
        """
        Get a list of software profile partitions from the db.

            Returns:
                [partition]
            Throws:
                SoftwareProfileNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            dbSoftwareProfile = self._softwareProfilesDbHandler.\
                getSoftwareProfile(session, softwareProfileName)
            return self.getTortugaObjectList(Partition,
                                             dbSoftwareProfile.partitions)
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def addPackage(self, packageName, softwareProfileName):
        """
        Add software profile package.

            Returns:
                packageId
            Throws:
                PackageAlreadyExists
                SoftwareProfileNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            self._softwareProfilesDbHandler.addPackageToSoftwareProfile(
                session, packageName, softwareProfileName)

            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def deletePackage(self, packageName, softwareProfileName):
        """
        Delete node from the db.

            Returns:
                None
            Throws:
                PackageNotFound
                SoftwareProfileNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            self._softwareProfilesDbHandler.\
                deletePackageFromSoftwareProfile(
                    session, packageName, softwareProfileName)
            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def addPartition(self, partitionName, softwareProfileName):
        """
        Add software profile partition.

            Returns:
                partitionId
            Throws:
                PartitionAlreadyExists
                SoftwareProfileNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            self._softwareProfilesDbHandler.\
                addPartitionToSoftwareProfile(
                    session, partitionName, softwareProfileName)

            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def deletePartition(self, partitionName, softwareProfileName):
        """
        Delete node from the db.

            Returns:
                None
            Throws:
                PartitionNotFound
                SoftwareProfileNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            self._softwareProfilesDbHandler.\
                deletePartitionFromSoftwareProfile(
                    session, partitionName, softwareProfileName)
            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def addUsableHardwareProfileToSoftwareProfile(self, hardwareProfileName,
                                                  softwareProfileName):
        """
         Add hardwareProfile to softwareProfile

            Returns:
                SoftwareUsesHardwareId
            Throws:
                HardwareProfileNotFound
                SoftwareProfileNotFound
                SoftwareUsesHardwareAlreadyExists
                DbError
        """

        session = DbManager().openSession()

        try:
            swUsesHwId = self._softwareProfilesDbHandler.\
                addUsableHardwareProfileToSoftwareProfile(
                    session, hardwareProfileName, softwareProfileName)
            session.commit()
            return swUsesHwId
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def deleteUsableHardwareProfileFromSoftwareProfile(self,
                                                       hardwareProfileName,
                                                       softwareProfileName):
        """
        Delete hardwareProfile from softwareProfile

            Returns:
                None
            Throws:
                HardwareProfileNotFound
                SoftwareProfileNotFound
                SoftwareUsesHardwareNotFound
                DbError
        """

        session = DbManager().openSession()

        try:
            self._softwareProfilesDbHandler.\
                deleteUsableHardwareProfileFromSoftwareProfile(
                    session, hardwareProfileName, softwareProfileName)

            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def addAdmin(self, softwareProfileName, adminUsername):
        """ Add an admin to this software profile """
        session = DbManager().openSession()

        try:
            dbAdmin = self._adminsDbHandler.getAdmin(session, adminUsername)

            dbSoftwareProfile = self._softwareProfilesDbHandler.\
                getSoftwareProfile(session, softwareProfileName)

            if dbAdmin not in dbSoftwareProfile.admins:
                dbSoftwareProfile.admins.append(dbAdmin)
            else:
                raise AdminAlreadyExists(
                    'Admin [%s] already associated with %s' %
                    (adminUsername, softwareProfileName))
            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def deleteAdmin(self, softwareProfileName, adminUsername):
        """ Delete an admin from a software profile """

        session = DbManager().openSession()
        try:
            dbAdmin = self._adminsDbHandler.getAdmin(session, adminUsername)

            dbSoftwareProfile = self._softwareProfilesDbHandler.\
                getSoftwareProfile(session, softwareProfileName)

            if dbAdmin in dbSoftwareProfile.admins:
                dbSoftwareProfile.admins.remove(dbAdmin)
            else:
                raise AdminNotFound(
                    'Admin [%s] not associated with software profile'
                    ' [%s]' % (adminUsername, softwareProfileName))

            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def updateSoftwareProfile(self, softwareProfileObject):
        """ Update a software profile """

        session = DbManager().openSession()

        try:
            dbSoftwareProfile = self._softwareProfilesDbHandler.\
                getSoftwareProfileById(
                    session, softwareProfileObject.getId())
            self.__populateSoftwareProfile(session, softwareProfileObject,
                                           dbSoftwareProfile)
            session.commit()
        except TortugaException as ex:
            session.rollback()
            raise
        except Exception as ex:
            session.rollback()
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()

    def __populateSoftwareProfile(self,
                                  session,
                                  softwareProfile,
                                  dbSoftwareProfile=None):
        """
        Helper function for creating/updating dbSoftwareProfile Object
        """

        # Validate object
        if softwareProfile.getName() is None:
            raise UpdateSoftwareProfileFailed('Software profile name required')

        if softwareProfile.getType() is None:
            raise UpdateSoftwareProfileFailed(
                'Software profile must have valid type')

        osInfo = softwareProfile.getOsInfo()
        if osInfo is None or osInfo.getName() is None:
            raise UpdateSoftwareProfileFailed(
                'Software profile must have valid operating system')

        if dbSoftwareProfile is None:
            dbSoftwareProfile = SoftwareProfiles()

        dbOs = self._osDbHandler.addOsIfNotFound(session, osInfo)

        dbSoftwareProfile.name = softwareProfile.getName()
        dbSoftwareProfile.description = softwareProfile.getDescription()
        dbSoftwareProfile.kernel = softwareProfile.getKernel()
        dbSoftwareProfile.kernelParams = softwareProfile.getKernelParams()
        dbSoftwareProfile.initrd = softwareProfile.getInitrd()
        dbSoftwareProfile.os = dbOs
        dbSoftwareProfile.type = softwareProfile.getType()
        dbSoftwareProfile.minNodes = softwareProfile.getMinNodes()
        dbSoftwareProfile.isIdle = softwareProfile.getIsIdle()

        # Add packages
        packages = {}
        for package in softwareProfile.getPackages():
            # This is a new package
            dbPackage = Packages()

            dbPackage.name = package.getName()
            if packages.get(dbPackage.name) is not None:
                # Duplicate package ...error
                raise UpdateSoftwareProfileFailed(
                    'Duplicate package [%s] found' % (dbPackage.name))

            packages[dbPackage.name] = dbPackage

        # Add partitions
        partitions = {}
        for partition in softwareProfile.getPartitions():
            # This is a new partition
            dbPartition = Partitions()

            dbPartition.name = partition.getName()
            dbPartition.device = partition.getDevice()
            dbPartition.mountPoint = partition.getMountPoint()
            dbPartition.fsType = partition.getFsType()
            dbPartition.size = partition.getSize()
            dbPartition.options = partition.getOptions()
            dbPartition.preserve = partition.getPreserve()
            dbPartition.bootLoader = partition.getBootLoader()
            dbPartition.diskSize = partition.getDiskSize()
            dbPartition.directAttachment = partition.getDirectAttachment()
            dbPartition.indirectAttachment = partition.\
                getIndirectAttachment()
            dbPartition.sanVolume = partition.getSanVolume()

            if not dbPartition.name:
                raise InvalidPartitionScheme(
                    'Invalid partition in software profile:'
                    ' missing or empty name')

            if not dbPartition.device:
                raise InvalidPartitionScheme(
                    'Invalid partition in software profile:'
                    ' missing or empty device')

            if not dbPartition.fsType:
                raise InvalidPartitionScheme(
                    'Invalid partition [%s/%s] in software profile:'
                    ' missing or empty fsType' %
                    (dbPartition.name, dbPartition.device))

            if dbPartition.size is None:
                raise InvalidPartitionScheme(
                    'Invalid partition [%s/%s] in software profile:'
                    ' missing size' % (dbPartition.name, dbPartition.device))

            if partitions.get(
                (dbPartition.name, dbPartition.device)) is not None:
                # Duplicate partition ...error
                raise UpdateSoftwareProfileFailed(
                    'Duplicate partition [%s/%s] found' %
                    (dbPartition.name, dbPartition.device))

            try:
                int(dbPartition.size)
            except ValueError:
                raise InvalidPartitionScheme(
                    'Invalid partition [%s/%s] in software profile:'
                    ' non-integer size' %
                    (dbPartition.name, dbPartition.device))

            try:
                if dbPartition.diskSize is not None:
                    int(dbPartition.diskSize)
            except ValueError:
                raise InvalidPartitionScheme(
                    'Invalid partition [%s/%s] in software profile:'
                    ' non-integer disk size' %
                    (dbPartition.name, dbPartition.device))

            bGrow = partition.getGrow()
            if bGrow is not None:
                dbPartition.grow = bGrow

            maxSize = partition.getMaxSize()
            if maxSize is not None:
                dbPartition.maxSize = maxSize

            partitions[(dbPartition.name, dbPartition.device)] = \
                dbPartition

        # Delete out the old ones
        dbSoftwareProfile.partitions = []
        dbSoftwareProfile.packages = []

        session.flush()

        dbSoftwareProfile.partitions = list(partitions.values())
        dbSoftwareProfile.packages = list(packages.values())

        session.add(dbSoftwareProfile)

        session.flush()

        return dbSoftwareProfile

    def copySoftwareProfile(self, srcSoftwareProfileName,
                            dstSoftwareProfileName):
        session = DbManager().openSession()

        srcSoftwareProfile = self.getSoftwareProfile(srcSoftwareProfileName, {
            'partitions': True,
            'packages': True,
            'components': True,
        })

        dstSoftwareProfile = self.getSoftwareProfile(srcSoftwareProfileName)
        dstSoftwareProfile.setName(dstSoftwareProfileName)
        newDescription = 'Copy of %s' % (dstSoftwareProfile.getDescription())
        dstSoftwareProfile.setDescription(newDescription)

        # partitions
        dstSoftwareProfile.setPartitions(srcSoftwareProfile.getPartitions())

        # packages
        dstSoftwareProfile.setPackages(srcSoftwareProfile.getPackages())

        # Finally add the software profile
        dstSoftwareProfile = self.addSoftwareProfile(dstSoftwareProfile,
                                                     session)

        # Enable components separately
        srcCompList = self.getEnabledComponentList(srcSoftwareProfileName)

        for srcComp in srcCompList:
            if srcComp.getKit().getIsOs() or srcComp.getName() == 'core':
                self._softwareProfilesDbHandler.\
                    addComponentToSoftwareProfileEx(
                        session, srcComp.getId(), dstSoftwareProfile)

        session.commit()

    def getUsableNodes(self, name):
        """
        Return list of nodes with same software/hardware profile mapping
        as the specified software profile.
        """

        session = DbManager().openSession()

        try:
            dbSoftwareProfile = self._softwareProfilesDbHandler.\
                getSoftwareProfile(session, name)

            nodes = [
                dbNode
                for dbHardwareProfile in dbSoftwareProfile.hardwareprofiles
                for dbNode in dbHardwareProfile.nodes
            ]

            return self.getTortugaObjectList(Node, nodes)
        except TortugaException as ex:
            raise
        except Exception as ex:
            self.getLogger().exception('%s' % ex)
            raise
        finally:
            DbManager().closeSession()