diff --git a/client/ayon_deadline/addon.py b/client/ayon_deadline/addon.py index f697c65974..e5ee0d483c 100644 --- a/client/ayon_deadline/addon.py +++ b/client/ayon_deadline/addon.py @@ -38,6 +38,11 @@ def initialize(self, studio_settings): self.deadline_servers_info = deadline_servers_info + self._pools_per_server = {} + self._limit_groups_per_server = {} + self._groups_per_server = {} + self._machines_per_server = {} + def get_plugin_paths(self): """Deadline plugin paths.""" # Note: We are not returning `publish` key because we have overridden @@ -67,26 +72,146 @@ def get_deadline_pools(webservice, auth=None, log=None): RuntimeError: If deadline webservice is unreachable. """ + endpoint = "{}/api/pools?NamesOnly=true".format(webservice) + return DeadlineAddon._get_deadline_info( + endpoint, auth, log, item_type="pools") + + @staticmethod + def get_deadline_groups(webservice, auth=None, log=None): + """Get Groups from Deadline. + Args: + webservice (str): Server url. + auth (Optional[Tuple[str, str]]): Tuple containing username, + password + log (Optional[Logger]): Logger to log errors to, if provided. + Returns: + List[str]: Limit Groups. + Throws: + RuntimeError: If deadline webservice is unreachable. + + """ + endpoint = "{}/api/groups".format(webservice) + return DeadlineAddon._get_deadline_info( + endpoint, auth, log, item_type="groups") + + @staticmethod + def get_deadline_limit_groups(webservice, auth=None, log=None): + """Get Limit Groups from Deadline. + Args: + webservice (str): Server url. + auth (Optional[Tuple[str, str]]): Tuple containing username, + password + log (Optional[Logger]): Logger to log errors to, if provided. + Returns: + List[str]: Limit Groups. + Throws: + RuntimeError: If deadline webservice is unreachable. + + """ + endpoint = "{}/api/limitgroups?NamesOnly=true".format(webservice) + return DeadlineAddon._get_deadline_info( + endpoint, auth, log, item_type="limitgroups") + + @staticmethod + def get_deadline_workers(webservice, auth=None, log=None): + """Get Groups from Deadline. + Args: + webservice (str): Server url. + auth (Optional[Tuple[str, str]]): Tuple containing username, + password + log (Optional[Logger]): Logger to log errors to, if provided. + Returns: + List[str]: Limit Groups. + Throws: + RuntimeError: If deadline webservice is unreachable. + + """ + endpoint = "{}/api/slaves?NamesOnly=true".format(webservice) + return DeadlineAddon._get_deadline_info( + endpoint, auth, log, item_type="workers") + + @staticmethod + def _get_deadline_info(endpoint, auth=None, log=None, item_type=None): from .abstract_submit_deadline import requests_get if not log: log = Logger.get_logger(__name__) - argument = "{}/api/pools?NamesOnly=true".format(webservice) try: kwargs = {} if auth: kwargs["auth"] = auth - response = requests_get(argument, **kwargs) + response = requests_get(endpoint, **kwargs) except requests.exceptions.ConnectionError as exc: - msg = 'Cannot connect to DL web service {}'.format(webservice) + msg = 'Cannot connect to DL web service {}'.format(endpoint) log.error(msg) six.reraise( DeadlineWebserviceError, DeadlineWebserviceError('{} - {}'.format(msg, exc)), sys.exc_info()[2]) if not response.ok: - log.warning("No pools retrieved") + log.warning(f"No {item_type} retrieved") return [] return response.json() + + def pools_per_server(self, server_name): + pools = self._pools_per_server.get(server_name) + if pools is None: + dl_server_info = self.deadline_servers_info.get(server_name) + + auth = (dl_server_info["default_username"], + dl_server_info["default_password"]) + pools = self.get_deadline_pools( + dl_server_info["value"], + auth + ) + self._pools_per_server[server_name] = pools + + return pools + + def groups_per_server(self, server_name): + groups = self._groups_per_server.get(server_name) + if groups is None: + dl_server_info = self.deadline_servers_info.get(server_name) + + auth = (dl_server_info["default_username"], + dl_server_info["default_password"]) + groups = self.get_deadline_groups( + dl_server_info["value"], + auth + ) + self._groups_per_server[server_name] = groups + + return groups + + def limit_groups_per_server(self, server_name): + limit_groups = self._limit_groups_per_server.get(server_name) + if limit_groups is None: + dl_server_info = self.deadline_servers_info.get(server_name) + + auth = (dl_server_info["default_username"], + dl_server_info["default_password"]) + limit_groups = self.get_deadline_limit_groups( + dl_server_info["value"], + auth + ) + self._limit_groups_per_server[server_name] = limit_groups + + return limit_groups + + def machines_per_server(self, server_name): + machines = self._machines_per_server.get(server_name) + if machines is None: + dl_server_info = self.deadline_servers_info.get(server_name) + + auth = (dl_server_info["default_username"], + dl_server_info["default_password"]) + machines = self.get_deadline_workers( + dl_server_info["value"], + auth + ) + self._machines_per_server[server_name] = machines + + return machines + diff --git a/client/ayon_deadline/lib.py b/client/ayon_deadline/lib.py index 8231f85b72..aa31467110 100644 --- a/client/ayon_deadline/lib.py +++ b/client/ayon_deadline/lib.py @@ -373,8 +373,9 @@ def from_dict(cls, data: Dict) -> 'AYONDeadlineJobInfo': "MachineLimit": data["machine_limit"], "ConcurrentTasks": data["concurrent_tasks"], "Frames": data["frames"], - "Pool": data["primary_pool"], - "SecondaryPool": data["secondary_pool"], + "Group": cls._sanitize(data["group"]), + "Pool": cls._sanitize(data["primary_pool"]), + "SecondaryPool": cls._sanitize(data["secondary_pool"]), # fields needed for logic, values unavailable during collection "UsePublished": data["use_published"], @@ -400,3 +401,15 @@ def add_instance_job_env_vars(self, instance): def to_json(self) -> str: """Serialize the dataclass instance to a JSON string.""" return json.dumps(asdict(self)) + + @classmethod + def _sanitize(cls, value) -> str: + if isinstance(value, str): + if value == "none": + return None + if isinstance(value, list): + filtered = [] + for val in value: + if val and val != "none": + filtered.append(val) + return filtered diff --git a/client/ayon_deadline/plugins/publish/global/collect_jobinfo.py b/client/ayon_deadline/plugins/publish/global/collect_jobinfo.py index 32c0f2a2b1..0884d217f0 100644 --- a/client/ayon_deadline/plugins/publish/global/collect_jobinfo.py +++ b/client/ayon_deadline/plugins/publish/global/collect_jobinfo.py @@ -37,6 +37,9 @@ class CollectJobInfo(pyblish.api.InstancePlugin, AYONPyblishPluginMixin): profiles = [] pool_enum_values = [] + group_enum_values = [] + limit_group_enum_values = [] + machines_enum_values = [] def process(self, instance): attr_values = self._get_jobinfo_defaults(instance) @@ -68,7 +71,7 @@ def _handle_additional_jobinfo(self,attr_values, job_info): def _handle_machine_list(self, attr_values, job_info): machine_list = attr_values["machine_list"] if machine_list: - if job_info.MachineListDeny: + if attr_values["machine_list_deny"]: job_info.Blacklist = machine_list else: job_info.Whitelist = machine_list @@ -83,18 +86,28 @@ def apply_settings(cls, project_settings): addons_manager = AddonsManager() deadline_addon = addons_manager["deadline"] deadline_server_name = settings["deadline_server"] - dl_server_info = deadline_addon.deadline_servers_info.get( - deadline_server_name) - - auth = (dl_server_info["default_username"], - dl_server_info["default_password"]) - pools = deadline_addon.get_deadline_pools( - dl_server_info["value"], - auth - ) + pools = deadline_addon.pools_per_server(deadline_server_name) for pool in pools: cls.pool_enum_values.append({"value": pool, "label": pool}) + groups = deadline_addon.groups_per_server(deadline_server_name) + for group in groups: + cls.group_enum_values.append({"value": group, "label": group}) + + limit_groups = ( + deadline_addon.limit_groups_per_server(deadline_server_name)) + if not limit_groups: + limit_groups.append("none") # enum cannot be empty + for limit_group in limit_groups: + cls.limit_group_enum_values.append( + {"value": limit_group, "label": limit_group}) + + machines = ( + deadline_addon.machines_per_server(deadline_server_name)) + for machine in machines: + cls.machines_enum_values.append( + {"value": machine, "label": machine}) + @classmethod def get_attr_defs_for_instance(cls, create_context, instance): host_name = create_context.host_name @@ -153,8 +166,22 @@ def _get_artist_overrides(cls, overrides, profile): default_values = {} for key in overrides: default_value = profile[key] + if key == "machine_limit": + filtered = [] + for value in default_value: + if value in cls.machines_enum_values: + filtered.append(value) + default_value = filtered + if key == "limit_groups": + filtered = [] + for value in default_value: + if value in cls.limit_group_enum_values: + filtered.append(value) + default_value = filtered if isinstance(default_value, list): default_value = ",".join(default_value) + if key == "group" and default_value not in cls.group_enum_values: + default_value = "" default_values[key] = default_value attr_defs = [ @@ -177,17 +204,18 @@ def _get_artist_overrides(cls, overrides, profile): label="Department", default=default_values.get("department") ), - TextDef( + EnumDef( "group", label="Group", - default=default_values.get("group") + default=default_values.get("group"), + items=cls.group_enum_values, ), - TextDef( + EnumDef( "limit_groups", label="Limit Groups", - # multiline=True, TODO - some DCC might have issues with storing multi lines + multiselection=True, default=default_values.get("limit_groups"), - placeholder="limit1,limit2" + items=cls.limit_group_enum_values, ), EnumDef( "primary_pool", @@ -201,10 +229,12 @@ def _get_artist_overrides(cls, overrides, profile): default="none", items=cls.pool_enum_values, ), - TextDef( + EnumDef( "machine_list", label="Machine list", - default=default_values.get("machine_list") + multiselection=True, + default=default_values.get("machine_list"), + items=cls.machines_enum_values, ), BoolDef( "machine_list_deny",