@@ -321,6 +321,39 @@ def computer_setup(ctx, non_interactive, **kwargs):
321321 echo .echo_report (f' verdi -p { profile .name } computer configure { computer .transport_type } { computer .label } ' )
322322
323323
324+ @verdi_computer .command ('setup-many' )
325+ @click .argument ('config_files' , nargs = - 1 , required = True , type = click .Path (exists = True , path_type = pathlib .Path ))
326+ @with_dbenv ()
327+ def computer_setup_many (config_files ):
328+ """Create multiple computers from YAML configuration files."""
329+ import yaml
330+
331+ from aiida .common .exceptions import IntegrityError
332+ from aiida .orm .utils .builders .computer import ComputerBuilder
333+
334+ for config_path in config_files :
335+ try :
336+ with open (config_path , 'r' , encoding = 'utf-8' ) as f :
337+ config_data = yaml .safe_load (f )
338+
339+ computer_builder = ComputerBuilder (** config_data )
340+ computer = computer_builder .new ()
341+ computer .store ()
342+
343+ echo .echo_success (f'Computer<{ computer .pk } > { computer .label } created' )
344+ except IntegrityError as e :
345+ if 'UNIQUE constraint failed: db_dbcomputer.label' in str (e ):
346+ msg = (
347+ f'Error processing { config_path } : Computer with label "{ config_data .get ("label" , "unknown" )} "'
348+ 'already exists'
349+ )
350+ echo .echo_error (msg )
351+ else :
352+ echo .echo_error (f'Error processing { config_path } : Database integrity error - { e } ' )
353+ except Exception as e :
354+ echo .echo_error (f'Error processing { config_path } : { e } ' )
355+
356+
324357@verdi_computer .command ('duplicate' )
325358@arguments .COMPUTER (callback = set_computer_builder )
326359@options_computer .LABEL (contextual_default = partial (get_parameter_default , 'label' ))
@@ -744,222 +777,91 @@ def computer_export():
744777
745778
746779@computer_export .command ('setup' )
747- @click . argument ( 'computers_and_output' , nargs = - 1 , required = False )
748- @click .option ( '-a' , '--all' , 'export_all' , is_flag = True , help = 'Export all computers.' )
780+ @arguments . COMPUTER ( )
781+ @arguments . OUTPUT_FILE ( type = click .Path ( exists = False , path_type = pathlib . Path ), required = False )
749782@options .OVERWRITE ()
750783@options .SORT ()
751784@with_dbenv ()
752- def computer_export_setup (computers_and_output , export_all , overwrite , sort ):
753- """Export computer setup(s) to YAML file(s).
754-
755- Usage:
756- - verdi computer export setup COMPUTER_ID [COMPUTER_ID ...] [OUTPUT_FILE]
757- - verdi computer export setup --all
758-
759- If no output file is given, default names are created based on the computer labels.
760- Custom output filename can only be provided when exporting a single computer.
761- """
785+ def computer_export_setup (computer , output_file , overwrite , sort ):
786+ """Export computer setup to a YAML file."""
762787 import yaml
763788
764- from aiida import orm
765- from aiida .common import NotExistent
766- from aiida .orm import load_computer
767-
768- # Handle --all option
769- if export_all :
770- if computers_and_output :
771- echo .echo_critical ('Cannot specify both --all and individual computers.' )
772-
773- # Get all computers
774- query = orm .QueryBuilder ()
775- query .append (orm .Computer )
776- computers = [computer for [computer ] in query .all ()]
777- if not computers :
778- echo .echo_report ('No computers found in the database.' )
779- return
780- output_file = None
781- else :
782- # Parse computers and potential output file
783- if not computers_and_output :
784- echo .echo_critical ('Must specify either --all or individual computer identifiers.' )
785-
786- computers = []
787- output_file = None
788-
789- # Try to parse all arguments as computers first
790- for i , arg in enumerate (computers_and_output ):
791- try :
792- computer = load_computer (arg )
793- computers .append (computer )
794- except NotExistent :
795- # This argument is not a valid computer identifier
796- if i == len (computers_and_output ) - 1 :
797- # Last argument and not a computer - treat as output file
798- output_file = pathlib .Path (arg )
799- break
800- else :
801- # Not last argument and not a computer - error
802- echo .echo_critical (f'Invalid computer identifier: { arg } ' )
803-
804- if not computers :
805- echo .echo_critical ('No valid computer identifiers provided.' )
806-
807- # Validate output file usage
808- if output_file and len (computers ) > 1 :
809- msg = 'Custom output filename can only be provided if a single computer is being exported.'
810- raise click .BadParameter (msg )
811-
812- # Export each computer
813- for computer in computers :
814- computer_setup = {
815- 'label' : computer .label ,
816- 'hostname' : computer .hostname ,
817- 'description' : computer .description ,
818- 'transport' : computer .transport_type ,
819- 'scheduler' : computer .scheduler_type ,
820- 'shebang' : computer .get_shebang (),
821- 'work_dir' : computer .get_workdir (),
822- 'mpirun_command' : ' ' .join (computer .get_mpirun_command ()),
823- 'mpiprocs_per_machine' : computer .get_default_mpiprocs_per_machine (),
824- 'default_memory_per_machine' : computer .get_default_memory_per_machine (),
825- 'use_double_quotes' : computer .get_use_double_quotes (),
826- 'prepend_text' : computer .get_prepend_text (),
827- 'append_text' : computer .get_append_text (),
828- }
829-
830- # Determine the output file for this specific computer
831- if output_file is None :
832- current_output_file = pathlib .Path (f'{ computer .label } -setup.yaml' )
833- else :
834- current_output_file = output_file
789+ computer_setup = {
790+ 'label' : computer .label ,
791+ 'hostname' : computer .hostname ,
792+ 'description' : computer .description ,
793+ 'transport' : computer .transport_type ,
794+ 'scheduler' : computer .scheduler_type ,
795+ 'shebang' : computer .get_shebang (),
796+ 'work_dir' : computer .get_workdir (),
797+ 'mpirun_command' : ' ' .join (computer .get_mpirun_command ()),
798+ 'mpiprocs_per_machine' : computer .get_default_mpiprocs_per_machine (),
799+ 'default_memory_per_machine' : computer .get_default_memory_per_machine (),
800+ 'use_double_quotes' : computer .get_use_double_quotes (),
801+ 'prepend_text' : computer .get_prepend_text (),
802+ 'append_text' : computer .get_append_text (),
803+ }
835804
836- try :
837- validate_output_filename (output_file = current_output_file , overwrite = overwrite )
838- except (FileExistsError , IsADirectoryError ) as exception :
839- raise click .BadParameter (str (exception ), param_hint = 'OUTPUT_FILE' ) from exception
805+ if output_file is None :
806+ output_file = pathlib .Path (f'{ computer .label } -setup.yaml' )
807+ try :
808+ validate_output_filename (output_file = output_file , overwrite = overwrite )
809+ except (FileExistsError , IsADirectoryError ) as exception :
810+ raise click .BadParameter (str (exception ), param_hint = 'OUTPUT_FILE' ) from exception
840811
841- try :
842- current_output_file .write_text (yaml .dump (computer_setup , sort_keys = sort ), 'utf-8' )
843- except Exception as e :
844- error_traceback = traceback .format_exc ()
845- echo .CMDLINE_LOGGER .debug (error_traceback )
846- echo .echo_critical (
847- f'Unexpected error while exporting setup for Computer<{ computer .pk } > { computer .label } :\n ({ e !s} ).'
848- )
849- else :
850- echo .echo_success (
851- f"Computer<{ computer .pk } > { computer .label } setup exported to file '{ current_output_file } '."
852- )
812+ try :
813+ output_file .write_text (yaml .dump (computer_setup , sort_keys = sort ), 'utf-8' )
814+ except Exception as e :
815+ error_traceback = traceback .format_exc ()
816+ echo .CMDLINE_LOGGER .debug (error_traceback )
817+ echo .echo_critical (
818+ f'Unexpected error while exporting setup for Computer<{ computer .pk } > { computer .label } :\n ({ e !s} ).'
819+ )
820+ else :
821+ echo .echo_success (f"Computer<{ computer .pk } > { computer .label } setup exported to file '{ output_file } '." )
853822
854823
855824@computer_export .command ('config' )
856- @click . argument ( 'computers_and_output' , nargs = - 1 , required = False )
857- @click .option ( '-a' , '--all' , 'export_all' , is_flag = True , help = 'Export all computers.' )
825+ @arguments . COMPUTER ( )
826+ @arguments . OUTPUT_FILE ( type = click .Path ( exists = False , path_type = pathlib . Path ), required = False )
858827@options .USER (
859828 help = 'Email address of the AiiDA user from whom to export this computer (if different from default user).'
860829)
861830@options .OVERWRITE ()
862831@options .SORT ()
863832@with_dbenv ()
864- def computer_export_config (computers_and_output , export_all , user , overwrite , sort ):
865- """Export computer transport configuration(s) for a user to YAML file(s).
866-
867- Usage:
868- - verdi computer export config COMPUTER_ID [COMPUTER_ID ...] [OUTPUT_FILE]
869- - verdi computer export config --all
870-
871- If no output file is given, default names are created based on the computer labels.
872- Custom output filename can only be provided when exporting a single computer.
873- """
833+ def computer_export_config (computer , output_file , user , overwrite , sort ):
834+ """Export computer transport configuration for a user to a YAML file."""
874835 import yaml
875836
876- from aiida import orm
877- from aiida .common import NotExistent
878- from aiida .orm import load_computer
879-
880- # Handle --all option
881- if export_all :
882- if computers_and_output :
883- echo .echo_critical ('Cannot specify both --all and individual computers.' )
884-
885- # Get all configured computers
886- query = orm .QueryBuilder ()
887- query .append (orm .Computer )
888- all_computers = [computer for [computer ] in query .all ()]
889- computers = [comp for comp in all_computers if comp .is_configured ]
890- if not computers :
891- echo .echo_report ('No configured computers found in the database.' )
892- return
893- output_file = None
837+ if not computer .is_configured :
838+ echo .echo_critical (
839+ f'Computer<{ computer .pk } > { computer .label } configuration cannot be exported,'
840+ ' because computer has not been configured yet.'
841+ )
894842 else :
895- # Parse computers and potential output file
896- if not computers_and_output :
897- echo .echo_critical ('Must specify either --all or individual computer identifiers.' )
898-
899- computers = []
900- output_file = None
901-
902- # Try to parse all arguments as computers first
903- for i , arg in enumerate (computers_and_output ):
904- try :
905- computer = load_computer (arg )
906- computers .append (computer )
907- except NotExistent :
908- # This argument is not a valid computer identifier
909- if i == len (computers_and_output ) - 1 :
910- # Last argument and not a computer - treat as output file
911- output_file = pathlib .Path (arg )
912- break
913- else :
914- # Not last argument and not a computer - error
915- echo .echo_critical (f'Invalid computer identifier: { arg } ' )
916-
917- if not computers :
918- echo .echo_critical ('No valid computer identifiers provided.' )
919-
920- # Validate output file usage
921- if output_file and len (computers ) > 1 :
922- msg = 'Custom output filename can only be provided if a single computer is being exported.'
923- raise click .BadParameter (msg )
924-
925- # Export each computer
926- for computer in computers :
927- if not computer .is_configured :
928- echo .echo_warning (
929- f'Computer<{ computer .pk } > { computer .label } configuration cannot be exported,'
930- ' because computer has not been configured yet. Skipping.'
931- )
932- continue
933-
934- # Determine the output file for this specific computer
935843 if output_file is None :
936- current_output_file = pathlib .Path (f'{ computer .label } -config.yaml' )
937- else :
938- current_output_file = output_file
939-
844+ output_file = pathlib .Path (f'{ computer .label } -config.yaml' )
940845 try :
941- validate_output_filename (output_file = current_output_file , overwrite = overwrite )
846+ validate_output_filename (output_file = output_file , overwrite = overwrite )
942847 except (FileExistsError , IsADirectoryError ) as exception :
943848 raise click .BadParameter (str (exception ), param_hint = 'OUTPUT_FILE' ) from exception
944849
945- try :
946- computer_configuration = computer .get_configuration (user )
947- current_output_file .write_text (yaml .dump (computer_configuration , sort_keys = sort ), 'utf-8' )
948-
949- except Exception as exception :
950- error_traceback = traceback .format_exc ()
951- echo .CMDLINE_LOGGER .debug (error_traceback )
952- if user is None :
953- echo .echo_critical (
954- 'Unexpected error while exporting configuration for '
955- f'Computer<{ computer .pk } > { computer .label } : { exception !s} .'
956- )
957- else :
958- echo .echo_critical (
959- f'Unexpected error while exporting configuration for Computer<{ computer .pk } > { computer .label } '
960- f' and User<{ user .pk } > { user .email } : { exception !s} .'
961- )
850+ try :
851+ computer_configuration = computer .get_configuration (user )
852+ output_file .write_text (yaml .dump (computer_configuration , sort_keys = sort ), 'utf-8' )
853+
854+ except Exception as exception :
855+ error_traceback = traceback .format_exc ()
856+ echo .CMDLINE_LOGGER .debug (error_traceback )
857+ if user is None :
858+ echo .echo_critical (
859+ f'Unexpected error while exporting configuration for Computer<{ computer .pk } > { computer .label } : { exception !s} .' # noqa: E501
860+ )
962861 else :
963- echo .echo_success (
964- f'Computer<{ computer .pk } > { computer .label } configuration exported to file `{ current_output_file } `.'
862+ echo .echo_critical (
863+ f'Unexpected error while exporting configuration for Computer<{ computer .pk } > { computer .label } '
864+ f' and User<{ user .pk } > { user .email } : { exception !s} .'
965865 )
866+ else :
867+ echo .echo_success (f'Computer<{ computer .pk } > { computer .label } configuration exported to file `{ output_file } `.' )
0 commit comments