# -*- coding: utf-8 -*-
###########################################################################
# Copyright (c), The AiiDA team. All rights reserved. #
# This file is part of the AiiDA code. #
# #
# The code is hosted on GitHub at https://github.com/aiidateam/aiida_core #
# For further information on the license, see the LICENSE.txt file #
# For further information please visit http://www.aiida.net #
###########################################################################
import json
from copy import copy
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm.session import make_transient
from sqlalchemy.orm.attributes import flag_modified
from aiida.backends.sqlalchemy.models.computer import DbComputer
from aiida.backends.sqlalchemy.models.authinfo import DbAuthInfo
from aiida.common.exceptions import (NotExistent, ConfigurationError,
InvalidOperation, DbContentError)
from aiida.orm.implementation.general.computer import AbstractComputer, Util as ComputerUtil
from aiida.common.lang import override
[docs]class Computer(AbstractComputer):
@property
def uuid(self):
return unicode(self._dbcomputer.uuid)
@property
def pk(self):
return self._dbcomputer.id
@property
def id(self):
return self._dbcomputer.id
[docs] def __init__(self, **kwargs):
super(Computer, self).__init__()
uuid = kwargs.pop('uuid', None)
if uuid is not None:
if kwargs:
raise ValueError("If you pass a uuid, you cannot pass any "
"further parameter")
dbcomputer = DbComputer.query.filter_by(uuid=uuid).first()
if not dbcomputer:
raise NotExistent("No entry with UUID={} found".format(uuid))
self._dbcomputer = dbcomputer
else:
if 'dbcomputer' in kwargs:
dbcomputer = kwargs.pop('dbcomputer')
if not (isinstance(dbcomputer, DbComputer)):
raise TypeError("dbcomputer must be of type DbComputer")
self._dbcomputer = dbcomputer
if kwargs:
raise ValueError("If you pass a dbcomputer parameter, "
"you cannot pass any further parameter")
else:
self._dbcomputer = DbComputer()
# Set all remaining parameters, stop if unknown
self.set(**kwargs)
[docs] def set(self, **kwargs):
is_modified = False
for key, val in kwargs.iteritems():
if hasattr(self._dbcomputer, key):
setattr(self._dbcomputer, key, val)
else:
self._dbcomputer._metadata[key] = val
is_modified = True
if is_modified:
flag_modified(self._dbcomputer, "_metadata")
[docs] @classmethod
def list_names(cls):
from aiida.backends.sqlalchemy import get_scoped_session
session = get_scoped_session()
return session.query(DbComputer.name).all()
@property
def full_text_info(self):
ret_lines = []
ret_lines.append("Computer name: {}".format(self.name))
ret_lines.append(" * PK: {}".format(self.pk))
ret_lines.append(" * UUID: {}".format(self.uuid))
ret_lines.append(" * Description: {}".format(self.description))
ret_lines.append(" * Hostname: {}".format(self.hostname))
ret_lines.append(" * Enabled: {}".format("True" if self.is_enabled() else "False"))
ret_lines.append(" * Transport type: {}".format(self.get_transport_type()))
ret_lines.append(" * Scheduler type: {}".format(self.get_scheduler_type()))
ret_lines.append(" * Work directory: {}".format(self.get_workdir()))
ret_lines.append(" * Shebang: {}".format(self.get_shebang()))
ret_lines.append(" * mpirun command: {}".format(" ".join(
self.get_mpirun_command())))
def_cpus_machine = self.get_default_mpiprocs_per_machine()
if def_cpus_machine is not None:
ret_lines.append(" * Default number of cpus per machine: {}".format(
def_cpus_machine))
ret_lines.append(" * Used by: {} nodes".format(
len(self.dbcomputer.dbnodes)))
ret_lines.append(" * prepend text:")
if self.get_prepend_text().strip():
for l in self.get_prepend_text().split('\n'):
ret_lines.append(" {}".format(l))
else:
ret_lines.append(" # No prepend text.")
ret_lines.append(" * append text:")
if self.get_append_text().strip():
for l in self.get_append_text().split('\n'):
ret_lines.append(" {}".format(l))
else:
ret_lines.append(" # No append text.")
return "\n".join(ret_lines)
@property
def to_be_stored(self):
return self._dbcomputer.id is None
[docs] @classmethod
def get(cls, computer):
return cls(dbcomputer=DbComputer.get_dbcomputer(computer))
[docs] @override
def copy(self):
from aiida.backends.sqlalchemy import get_scoped_session
session = get_scoped_session()
if self.to_be_stored:
raise InvalidOperation("You can copy a computer only after having stored it")
newdbcomputer = copy(self.dbcomputer)
make_transient(newdbcomputer)
session.add(newdbcomputer)
newobject = self.__class__(newdbcomputer)
return newobject
@property
def dbcomputer(self):
return self._dbcomputer
[docs] def store(self):
self.validate()
try:
self.dbcomputer.save(commit=True)
except SQLAlchemyError:
raise ValueError(
"Integrity error, probably the hostname already exists in the"
" DB")
return self
@property
def name(self):
return self.dbcomputer.name
@property
def description(self):
return self.dbcomputer.description
@property
def hostname(self):
return self.dbcomputer.hostname
[docs] def get_transport_params(self):
"""
Return transport params stored in dbcomputer instance
"""
return self.dbcomputer.transport_params
[docs] def set_transport_params(self, val):
# if self.to_be_stored:
try:
json.dumps(val) # Check if json compatible
self.dbcomputer.transport_params = val
except ValueError:
raise ValueError("The set of transport_params are not JSON-able")
if not self.to_be_stored:
self.dbcomputer.save()
# else:
# raise ModificationNotAllowed("Cannot set a property after having stored the entry")
[docs] def get_workdir(self):
try:
return self.dbcomputer.get_workdir()
except ConfigurationError:
# This happens the first time: I provide a reasonable default value
return "/scratch/{username}/aiida_run/"
[docs] def set_workdir(self, val):
pass
# if self.to_be_stored:
metadata = self._get_metadata()
metadata['workdir'] = val
self._set_metadata(metadata)
[docs] def get_shebang(self):
try:
return self.dbcomputer.get_shebang()
except ConfigurationError:
# This happens the first time: I provide a reasonable default value
return "#!/bin/bash"
[docs] def get_name(self):
return self.dbcomputer.name
[docs] def set_name(self, val):
self.dbcomputer.name = val
if not self.to_be_stored:
self.dbcomputer.save()
[docs] def get_hostname(self):
return self.dbcomputer.hostname
[docs] def set_hostname(self, val):
self.dbcomputer.hostname = val
if not self.to_be_stored:
self.dbcomputer.save()
[docs] def get_description(self):
return self.dbcomputer.description
[docs] def set_description(self, val):
self.dbcomputer.description = val
if not self.to_be_stored:
self.dbcomputer.save()
[docs] def get_calculations_on_computer(self):
from aiida.backends.sqlalchemy.models.node import DbNode
return DbNode.query.filter(
DbNode.dbcomputer_id == self.dbcomputer.id,
DbNode.type.like("calculation%")).all()
[docs] def is_enabled(self):
return self.dbcomputer.enabled
[docs] def get_dbauthinfo(self, user):
info = DbAuthInfo.query.filter_by(
dbcomputer=self.dbcomputer,
aiidauser=user).first()
if not info:
raise NotExistent("The user '{}' is not configured for "
"computer '{}'".format(
user.email, self.name))
return info
[docs] def is_user_enabled(self, user):
try:
dbauthinfo = self.get_dbauthinfo(user)
return dbauthinfo.enabled
except NotExistent:
# Return False if the user is not configured (in a sense,
# it is disabled for that user)
return False
[docs] def set_enabled_state(self, enabled):
self.dbcomputer.enabled = enabled
if not self.to_be_stored:
self.dbcomputer.save()
[docs] def get_scheduler_type(self):
return self.dbcomputer.scheduler_type
[docs] def set_scheduler_type(self, val):
self.dbcomputer.scheduler_type = val
if not self.to_be_stored:
self.dbcomputer.save()
[docs] def get_transport_type(self):
return self.dbcomputer.transport_type
[docs] def set_transport_type(self, val):
self.dbcomputer.transport_type = val
if not self.to_be_stored:
self.dbcomputer.save()
[docs]class Util(ComputerUtil):
[docs] @override
def delete_computer(self, pk):
"""
Delete the computer with the given pk.
:param pk: The computer pk.
"""
import aiida.backends.sqlalchemy
try:
session = aiida.backends.sqlalchemy.get_scoped_session()
session.query(DbComputer).get(pk).delete()
session.commit()
except SQLAlchemyError as e:
raise InvalidOperation("Unable to delete the requested computer: it is possible that there "
"is at least one node using this computer (original message: {})".format(
e.message
))