# -*- 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 os
from aiida.backends.sqlalchemy.models.computer import DbComputer
from aiida.common.exceptions import NotExistent, MultipleObjectsError, InvalidOperation
from aiida.orm.implementation.general.code import AbstractCode
from aiida.orm.implementation.sqlalchemy.computer import Computer
[docs]class Code(AbstractCode):
[docs] @classmethod
def get(cls, pk=None, label=None, machinename=None):
return super(Code, cls).get(pk, label, machinename)
[docs] @classmethod
def get_from_string(cls, code_string):
return super(Code, cls).get_from_string(code_string)
[docs] @classmethod
def list_for_plugin(cls, plugin, labels=True):
return super(Code, cls).list_for_plugin(plugin, labels)
[docs] def set_remote_computer_exec(self, remote_computer_exec):
"""
Set the code as remote, and pass the computer on which it resides
and the absolute path on that computer.
Args:
remote_computer_exec: a tuple (computer, remote_exec_path), where
computer is a aiida.orm.Computer or an
aiida.backends.djsite.db.models.DbComputer object, and
remote_exec_path is the absolute path of the main executable on
remote computer.
"""
if (not isinstance(remote_computer_exec, (list, tuple))
or len(remote_computer_exec) != 2):
raise ValueError("remote_computer_exec must be a list or tuple "
"of length 2, with machine and executable "
"name")
computer, remote_exec_path = tuple(remote_computer_exec)
if not os.path.isabs(remote_exec_path):
raise ValueError("exec_path must be an absolute path (on the remote machine)")
if not isinstance(computer, Computer):
raise TypeError("Computer must be of type Computer, got '{}'".format(type(computer)))
self._set_remote()
self.set_computer(computer)
self._set_attr('remote_exec_path', remote_exec_path)
[docs] def _set_local(self):
"""
Set the code as a 'local' code, meaning that all the files belonging to the code
will be copied to the cluster, and the file set with set_exec_filename will be
run.
It also deletes the flags related to the local case (if any)
"""
self._set_attr('is_local', True)
self._dbnode.dbcomputer = None
try:
self._del_attr('remote_exec_path')
except AttributeError:
pass
[docs] def can_run_on(self, computer):
"""
Return True if this code can run on the given computer, False otherwise.
Local codes can run on any machine; remote codes can run only on the machine
on which they reside.
TODO: add filters to mask the remote machines on which a local code can run.
"""
if self.is_local():
return True
else:
dbcomputer = computer
if isinstance(dbcomputer, Computer):
dbcomputer = dbcomputer.dbcomputer
if not isinstance(dbcomputer, DbComputer):
raise ValueError("computer must be either a Computer or DbComputer object")
dbcomputer = DbComputer.get_dbcomputer(computer)
return (dbcomputer.id ==
self.get_remote_computer().dbcomputer.id)
[docs]def delete_code(code):
"""
Delete a code from the DB.
Check before that there are no output nodes.
NOTE! Not thread safe... Do not use with many users accessing the DB
at the same time.
Implemented as a function on purpose, otherwise complicated logic would be
needed to set the internal state of the object after calling
computer.delete().
"""
if not isinstance(code, Code):
raise TypeError("code must be an instance of "
"aiida.orm.computer.Code")
existing_outputs = code.get_outputs()
if len(existing_outputs) != 0:
raise InvalidOperation(
"Unable to delete the requested code because it "
"has {} output links".format(len(existing_outputs)))
else:
repo_folder = code._repository_folder
from aiida.backends.sqlalchemy import get_scoped_session
session = get_scoped_session()
session.begin(subtransactions=True)
try:
code.dbnode.delete()
session.commit()
except:
session.rollback()
raise
# If all went well, erase also the folder
repo_folder.erase()