Skip to content

Commit 3a3788c

Browse files
committed
Merge branch 'release/10.0' into chore/dss10-clarify-get-variable-apis
2 parents ab0c7ef + acdcd4c commit 3a3788c

File tree

7 files changed

+279
-21
lines changed

7 files changed

+279
-21
lines changed

dataikuapi/dss/admin.py

Lines changed: 247 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -695,13 +695,18 @@ def set_definition(self, env):
695695
696696
* env.permissions, env.usableByAll, env.desc.owner
697697
* env.specCondaEnvironment, env.specPackageList, env.externalCondaEnvName, env.desc.installCorePackages,
698-
env.desc.installJupyterSupport, env.desc.yarnPythonBin
698+
env.desc.corePackagesSet, env.desc.installJupyterSupport, env.desc.yarnPythonBin, env.desc.yarnRBin
699+
env.desc.envSettings, env.desc.allContainerConfs, env.desc.containerConfs,
700+
env.desc.allSparkKubernetesConfs, env.desc.sparkKubernetesConfs
699701
700702
Fields that can be updated in automation node (where {version} is the updated version):
701703
702-
* env.permissions, env.usableByAll, env.owner
704+
* env.permissions, env.usableByAll, env.owner, env.envSettings
703705
* env.{version}.specCondaEnvironment, env.{version}.specPackageList, env.{version}.externalCondaEnvName,
704-
env.{version}.desc.installCorePackages, env.{version}.desc.installJupyterSupport, env.{version}.desc.yarnPythonBin
706+
env.{version}.desc.installCorePackages, env.{version}.corePackagesSet, env.{version}.desc.installJupyterSupport
707+
env.{version}.desc.yarnPythonBin, env.{version}.desc.yarnRBin, env.{version}.desc.allContainerConfs,
708+
env.{version}.desc.containerConfs, env.{version}.desc.allSparkKubernetesConfs,
709+
env.{version}.{version}.desc.sparkKubernetesConfs
705710
706711
Note: this call requires an API key with admin rights
707712
@@ -722,6 +727,39 @@ def get_version_for_project(self, project_key):
722727
return self.client._perform_json(
723728
"GET", "/admin/code-envs/%s/%s/%s/version" % (self.env_lang, self.env_name, project_key))
724729

730+
731+
def get_settings(self):
732+
"""
733+
Returns the settings of this code env as a :class:`DSSCodeEnvSettings`, or one of its subclasses.
734+
735+
Known subclasses of :class:`DSSCodeEnvSettings` include :class:`DSSDesignCodeEnvSettings`
736+
and :class:`DSSAutomationCodeEnvSettings`
737+
738+
You must use :meth:`~DSSCodeEnvSettings.save()` on the returned object to make your changes effective
739+
on the code env.
740+
741+
.. code-block:: python
742+
743+
# Example: setting the required packagd
744+
codeenv = client.get_code_env("PYTHON", "code_env_name")
745+
settings = codeenv.get_settings()
746+
settings.set_required_packages("dash==2.0.0", "bokeh<2.0")
747+
settings.save()
748+
# then proceed to update_packages()
749+
750+
:rtype: :class:`DSSCodeEnvSettings`
751+
"""
752+
data = self.client._perform_json(
753+
"GET", "/admin/code-envs/%s/%s" % (self.env_lang, self.env_name))
754+
755+
# you can't just use deploymentMode to check if it's an automation code
756+
# env, because some modes are common to both types of nodes. So we rely
757+
# on a non-null field that only the automation code envs have
758+
if data.get("versions", None) is not None:
759+
return DSSAutomationCodeEnvSettings(self, data)
760+
else:
761+
return DSSDesignCodeEnvSettings(self, data)
762+
725763

726764
########################################################
727765
# Code env actions
@@ -806,6 +844,171 @@ def get_log(self, log_name):
806844
"GET", "/admin/code-envs/%s/%s/logs/%s" % (self.env_lang, self.env_name, log_name))
807845

808846

847+
class DSSCodeEnvSettings(object):
848+
"""
849+
Base settings class for a DSS code env.
850+
Do not instantiate this class directly, use :meth:`DSSCodeEnv.get_settings`
851+
852+
Use :meth:`save` to save your changes
853+
"""
854+
855+
def __init__(self, codeenv, settings):
856+
self.codeenv = codeenv
857+
self.settings = settings
858+
859+
def get_raw(self):
860+
"""Get the raw code env settings as a dict"""
861+
return self.settings
862+
863+
@property
864+
def env_lang(self):
865+
return self.codeenv.env_lang
866+
867+
@property
868+
def env_name(self):
869+
return self.codeenv.env_name
870+
871+
def save(self):
872+
self.codeenv.client._perform_json(
873+
"PUT", "/admin/code-envs/%s/%s" % (self.env_lang, self.env_name), body=self.settings)
874+
875+
class DSSCodeEnvPackageListBearer(object):
876+
def get_required_packages(self, as_list=False):
877+
"""
878+
Return the list of required packages, as a single string
879+
880+
:param boolean as_list: if True, return the spec as a list of lines; if False, return as a single multiline string
881+
"""
882+
x = self.settings.get("specPackageList", "")
883+
return x.split('\n') if as_list else x
884+
def set_required_packages(self, *packages):
885+
"""
886+
Set the list of required packages
887+
"""
888+
self.settings["specPackageList"] = '\n'.join(packages)
889+
890+
def get_required_conda_spec(self, as_list=False):
891+
"""
892+
Return the list of required conda packages, as a single string
893+
894+
:param boolean as_list: if True, return the spec as a list of lines; if False, return as a single multiline string
895+
"""
896+
x = self.settings.get("specCondaEnvironment", "")
897+
return x.split('\n') if as_list else x
898+
def set_required_conda_spec(self, *spec):
899+
"""
900+
Set the list of required conda packages
901+
"""
902+
self.settings["specCondaEnvironment"] = '\n'.join(packages)
903+
904+
class DSSCodeEnvContainerConfsBearer(object):
905+
def get_built_for_all_container_confs(self):
906+
"""
907+
Return whether the code env creates an image for each container config
908+
"""
909+
return self.settings.get("allContainerConfs", False)
910+
def get_built_container_confs(self):
911+
"""
912+
Return the list of container configs for which the code env builds an image (if not all)
913+
"""
914+
return self.settings.get("containerConfs", [])
915+
def set_built_container_confs(self, *configs, **kwargs):
916+
"""
917+
Set the list of container configs for which the code env builds an image
918+
919+
:param boolean all: if True, an image is built for each config
920+
:param list configs: list of configuration names to build images for
921+
"""
922+
all = kwargs.get("all", False)
923+
self.settings['allContainerConfs'] = all
924+
if not all:
925+
self.settings['containerConfs'] = configs
926+
def built_for_all_spark_kubernetes_confs(self):
927+
"""
928+
Return whether the code env creates an image for each managed Spark over Kubernetes config
929+
"""
930+
return self.settings.get("allSparkKubernetesConfs", False)
931+
def get_built_spark_kubernetes_confs(self):
932+
"""
933+
Return the list of managed Spark over Kubernetes configs for which the code env builds an image (if not all)
934+
"""
935+
return self.settings.get("sparkKubernetesConfs", [])
936+
def set_built_spark_kubernetes_confs(self, *configs, **kwargs):
937+
"""
938+
Set the list of managed Spark over Kubernetes configs for which the code env builds an image
939+
940+
:param boolean all: if True, an image is built for each config
941+
:param list configs: list of configuration names to build images for
942+
"""
943+
all = kwargs.get("all", False)
944+
self.settings['allSparkKubernetesConfs'] = all
945+
if not all:
946+
self.settings['sparkKubernetesConfs'] = configs
947+
948+
949+
class DSSDesignCodeEnvSettings(DSSCodeEnvSettings, DSSCodeEnvPackageListBearer, DSSCodeEnvContainerConfsBearer):
950+
"""
951+
Base settings class for a DSS code env on a design node.
952+
Do not instantiate this class directly, use :meth:`DSSCodeEnv.get_settings`
953+
954+
Use :meth:`save` to save your changes
955+
"""
956+
957+
def __init__(self, codeenv, settings):
958+
super(DSSDesignCodeEnvSettings, self).__init__(codeenv, settings)
959+
960+
961+
class DSSAutomationCodeEnvSettings(DSSCodeEnvSettings, DSSCodeEnvContainerConfsBearer):
962+
"""
963+
Base settings class for a DSS code env on an automation node.
964+
Do not instantiate this class directly, use :meth:`DSSCodeEnv.get_settings`
965+
966+
Use :meth:`save` to save your changes
967+
"""
968+
969+
def __init__(self, codeenv, settings):
970+
super(DSSAutomationCodeEnvSettings, self).__init__(codeenv, settings)
971+
972+
973+
def get_version(self, version_id=None):
974+
"""
975+
Get a specific code env version (for versioned envs) or the single
976+
version
977+
978+
:param string version_id: for versioned code env, identifier of the desired version
979+
980+
:rtype: :class:`DSSAutomationCodeEnvVersionSettings`
981+
"""
982+
deployment_mode = self.settings.get("deploymentMode", None)
983+
if deployment_mode in ['AUTOMATION_SINGLE']:
984+
return DSSAutomationCodeEnvVersionSettings(self.codeenv, self.settings.get('currentVersion', {}))
985+
elif deployment_mode in ['AUTOMATION_VERSIONED']:
986+
versions = self.settings.get("versions", [])
987+
version_ids = [v.get('versionId') for v in versions]
988+
if version_id is None:
989+
raise Exception("A version id is required in a versioned code env. Existing ids: %s" % ', '.join(version_ids))
990+
for version in versions:
991+
if version_id == version.get("versionId"):
992+
return DSSAutomationCodeEnvVersionSettings(self.codeenv, version)
993+
raise Exception("Version %s not found in : %s" % (version_id, ', '.join(version_ids)))
994+
elif deployment_mode in ['PLUGIN_NON_MANAGED', 'PLUGIN_MANAGED', 'AUTOMATION_NON_MANAGED_PATH', 'EXTERNAL_CONDA_NAMED']:
995+
return DSSAutomationCodeEnvVersionSettings(self.codeenv, self.settings.get('noVersion', {}))
996+
else:
997+
raise Exception("Unexpected deployment mode %s for an automation node code env. Alter the settings directly with get_raw()", deployment_mode)
998+
999+
class DSSAutomationCodeEnvVersionSettings(DSSCodeEnvPackageListBearer):
1000+
"""
1001+
Base settings class for a DSS code env version on an automation node.
1002+
Do not instantiate this class directly, use :meth:`DSSAutomationCodeEnvSettings.get_version`
1003+
1004+
Use :meth:`save` on the :class:`DSSAutomationCodeEnvSettings` to save your changes
1005+
"""
1006+
1007+
def __init__(self, codeenv_settings, version_settings):
1008+
self.codeenv_settings = codeenv_settings
1009+
self.settings = version_settings
1010+
1011+
8091012
class DSSGlobalApiKey(object):
8101013
"""
8111014
A global API key on the DSS instance
@@ -1025,3 +1228,44 @@ def save(self):
10251228
Note: this call requires an API key with admin rights.
10261229
"""
10271230
return self.client._perform_empty("PUT", "/admin/variables/", body=self)
1231+
1232+
1233+
class DSSGlobalUsageSummary(object):
1234+
"""
1235+
The summary of the usage of the DSS instance.
1236+
Do not create this directly, use :meth:`dataikuapi.dss.DSSClient.get_global_usage_summary`
1237+
"""
1238+
def __init__(self, data):
1239+
self.data = data
1240+
1241+
@property
1242+
def raw(self):
1243+
return self.data
1244+
1245+
@property
1246+
def projects_count(self):
1247+
return self.data["projects"]
1248+
1249+
@property
1250+
def total_datasets_count(self):
1251+
return self.data["datasets"]["all"]
1252+
1253+
@property
1254+
def total_recipes_count(self):
1255+
return self.data["recipes"]["all"]
1256+
1257+
@property
1258+
def total_jupyter_notebooks_count(self):
1259+
return self.data["notebooks"]["nbJupyterNotebooks"]
1260+
1261+
@property
1262+
def total_sql_notebooks_count(self):
1263+
return self.data["notebooks"]["nbSqlNotebooks"]
1264+
1265+
@property
1266+
def total_scenarios_count(self):
1267+
return self.data["scenarios"]["all"]
1268+
1269+
@property
1270+
def total_active_with_trigger_scenarios_count(self):
1271+
return self.data["scenarios"]["activeWithTriggers"]

dataikuapi/dss/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def __init__(self, client, project_key):
135135
DSSAppInstance.__init__(self, client,project_key)
136136

137137
def close(self):
138-
self.get_as_project().delete(drop_data=True)
138+
self.get_as_project().delete(clear_managed_datasets=True)
139139

140140
def __enter__(self,):
141141
return self

dataikuapi/dss/project.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,25 @@ def move_to_folder(self, folder):
7979
# Project deletion
8080
########################################################
8181

82-
def delete(self, drop_data=False):
82+
def delete(self, clear_managed_datasets=False, clear_output_managed_folders=False, clear_job_and_scenario_logs=True, **kwargs):
8383
"""
8484
Delete the project
8585
8686
This call requires an API key with admin rights
8787
88-
:param bool drop_data: Should the data of managed datasets be dropped
88+
:param bool clear_managed_datasets: Should the data of managed datasets be cleared
89+
:param bool clear_output_managed_folders: Should the data of managed folders used as outputs of recipes be cleared
90+
:param bool clear_job_and_scenario_logs: Should the job and scenario logs be cleared
8991
"""
92+
# For backwards compatibility
93+
if 'drop_data' in kwargs and kwargs['drop_data']:
94+
clear_managed_datasets = True
95+
9096
return self.client._perform_empty(
91-
"DELETE", "/projects/%s" % self.project_key, params = {
92-
"dropData": drop_data
97+
"DELETE", "/projects/%s" % self.project_key, params={
98+
"clearManagedDatasets": clear_managed_datasets,
99+
"clearOutputManagedFolders": clear_output_managed_folders,
100+
"clearJobAndScenarioLogs": clear_job_and_scenario_logs
93101
})
94102

95103
########################################################

dataikuapi/dss/tools/codegen.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json, copy, re
2-
from dataikuapi.dss.recipe import *
3-
from dataikuapi.dss.dataset import *
2+
from ..recipe import *
3+
from ..dataset import *
44

55
class _IndentContext(object):
66
def __init__(self, flow_code_generator):

dataikuapi/dssclient.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
from requests import exceptions
55
from requests.auth import HTTPBasicAuth
66

7-
from dataikuapi.dss.notebook import DSSNotebook
7+
from .dss.notebook import DSSNotebook
88
from .dss.future import DSSFuture
99
from .dss.projectfolder import DSSProjectFolder
1010
from .dss.project import DSSProject
1111
from .dss.app import DSSApp
1212
from .dss.plugin import DSSPlugin
13-
from .dss.admin import DSSUser, DSSOwnUser, DSSGroup, DSSConnection, DSSGeneralSettings, DSSCodeEnv, DSSGlobalApiKey, DSSCluster, DSSInstanceVariables
13+
from .dss.admin import DSSUser, DSSOwnUser, DSSGroup, DSSConnection, DSSGeneralSettings, DSSCodeEnv, DSSGlobalApiKey, DSSCluster, DSSGlobalUsageSummary, DSSInstanceVariables
1414
from .dss.meaning import DSSMeaning
1515
from .dss.sqlquery import DSSSQLQuery
1616
from .dss.discussion import DSSObjectDiscussions
@@ -486,22 +486,28 @@ def create_connection(self, name, type, params=None, usable_by='ALL', allowed_gr
486486
# Code envs
487487
########################################################
488488

489-
def list_code_envs(self):
489+
def list_code_envs(self, as_objects=False):
490490
"""
491491
List all code envs setup on the DSS instance
492492
493493
Note: this call requires an API key with admin rights
494494
495+
:param boolean as_objects: if True, each returned item will be a :class:`dataikuapi.dss.future.DSSCodeEnv`
495496
:returns: a list of code envs. Each code env is a dict containing at least "name", "type" and "language"
496497
"""
497-
return self._perform_json(
498+
list = self._perform_json(
498499
"GET", "/admin/code-envs/")
500+
if as_objects:
501+
return [DSSCodeEnv(self, e.get("envLang"), e.get("envName")) for e in list]
502+
else:
503+
return list
499504

500505
def get_code_env(self, env_lang, env_name):
501506
"""
502507
Get a handle to interact with a specific code env
503508
504-
:param str name: the name of the desired code env
509+
:param env_lang: the language (PYTHON or R) of the new code env
510+
:param env_name: the name of the new code env
505511
:returns: A :class:`dataikuapi.dss.admin.DSSCodeEnv` code env handle
506512
"""
507513
return DSSCodeEnv(self, env_lang, env_name)
@@ -757,12 +763,12 @@ def log_custom_audit(self, custom_type, custom_params=None):
757763

758764
def get_global_usage_summary(self, with_per_project=False):
759765
"""
760-
Summarize the contents of the instance
761-
766+
Gets a summary of the global usage of this DSS instance (number of projects, datasets, ...)
762767
:returns: a summary object
763768
"""
764-
return self._perform_json(
769+
data = self._perform_json(
765770
"GET", "/admin/monitoring/global-usage-summary", params={'withPerProject':with_per_project})
771+
return DSSGlobalUsageSummary(data)
766772

767773
########################################################
768774
# Variables

0 commit comments

Comments
 (0)