# -*- 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
import collections
from django.db import IntegrityError, transaction
from django.core.exceptions import ObjectDoesNotExist
from aiida.backends.djsite.db.models import DbComputer
from aiida.common.lang import override
from aiida.orm.implementation.general.computer import AbstractComputer, Util as ComputerUtil
from aiida.common.exceptions import (NotExistent, ConfigurationError,
InvalidOperation, DbContentError)
from aiida.orm.implementation.general.utils import get_db_columns
[docs]class Computer(AbstractComputer):
@property
def uuid(self):
return unicode(self._dbcomputer.uuid)
@property
def pk(self):
return self._dbcomputer.pk
@property
def id(self):
return self._dbcomputer.pk
[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")
try:
dbcomputer = DbComputer.objects.get(uuid=uuid)
except ObjectDoesNotExist:
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):
for k, v in kwargs.iteritems():
try:
method = getattr(self, 'set_{}'.format(k))
except AttributeError:
raise ValueError("Unable to set '{0}', no set_{0} method "
"found".format(k))
if not isinstance(method, collections.Callable):
raise ValueError("Unable to set '{0}', set_{0} is not "
"callable!".format(k))
method(v)
[docs] @classmethod
def list_names(cls):
from aiida.backends.djsite.db.models import DbComputer
return list(DbComputer.objects.filter().values_list('name', flat=True))
@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.all())))
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.pk is None)
[docs] @classmethod
def get(cls, computer):
from aiida.backends.djsite.db.models import DbComputer
return cls(dbcomputer=DbComputer.get_dbcomputer(computer))
[docs] def copy(self):
from aiida.backends.djsite.db.models import DbComputer
if self.to_be_stored:
raise InvalidOperation(
"You can copy a computer only after having stored it")
newdbcomputer = DbComputer.objects.get(pk=self.dbcomputer.pk)
newdbcomputer.pk = None
newobject = self.__class__(newdbcomputer)
return newobject
@property
def dbcomputer(self):
return self._dbcomputer
[docs] def store(self):
# if self.to_be_stored:
# As a first thing, I check if the data is valid
self.validate()
try:
# transactions are needed here for Postgresql:
# https://docs.djangoproject.com/en/1.5/topics/db/transactions/#handling-exceptions-within-postgresql-transactions
sid = transaction.savepoint()
self.dbcomputer.save()
transaction.savepoint_commit(sid)
except IntegrityError:
transaction.savepoint_rollback(sid)
raise ValueError(
"Integrity error, probably the hostname already exists in the"
" DB")
# This is useful because in this way I can do
# c = Computer().store()
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):
try:
return json.loads(self.dbcomputer.transport_params)
except ValueError:
raise DbContentError(
"Error while reading transport_params for computer {}".format(
self.hostname))
[docs] def set_transport_params(self, val):
# if self.to_be_stored:
try:
self.dbcomputer.transport_params = json.dumps(val)
except ValueError:
raise ValueError("The set of transport_params are not JSON-able")
if not self.to_be_stored:
self.dbcomputer.save()
[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 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 set_workdir(self, val):
# if self.to_be_stored:
if not isinstance(val, basestring):
raise ValueError("Computer work directory needs to be string, got {}".format(val))
metadata = self._get_metadata()
metadata['workdir'] = val
self._set_metadata(metadata)
[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.djsite.db.models import DbNode
return DbNode.objects.filter(dbcomputer__name=self.name,
type__startswith='calculation')
[docs] def is_enabled(self):
return self.dbcomputer.enabled
[docs] def get_dbauthinfo(self, user):
from aiida.backends.djsite.db.models import DbAuthInfo
try:
return DbAuthInfo.objects.get(dbcomputer=self.dbcomputer,
aiidauser=user)
except ObjectDoesNotExist:
raise NotExistent("The user '{}' is not configured for "
"computer '{}'".format(
user.email, self.name))
[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.
"""
from django.db.models.deletion import ProtectedError
try:
DbComputer.objects.filter(pk=pk).delete()
except ProtectedError:
raise InvalidOperation("Unable to delete the requested computer: there"
"is at least one node using this computer")