Path: blob/main/singlestoredb/fusion/handlers/workspace.py
469 views
#!/usr/bin/env python31import json2from typing import Any3from typing import Dict4from typing import Optional56from .. import result7from ..handler import SQLHandler8from ..result import FusionSQLResult9from .utils import dt_isoformat10from .utils import get_workspace11from .utils import get_workspace_group12from .utils import get_workspace_manager131415class UseWorkspaceHandler(SQLHandler):16"""17USE WORKSPACE workspace [ with_database ];1819# Workspace20workspace = { workspace_id | workspace_name | current_workspace }2122# ID of workspace23workspace_id = ID '<workspace-id>'2425# Name of workspace26workspace_name = '<workspace-name>'2728# Current workspace29current_workspace = @@CURRENT3031# Name of database32with_database = WITH DATABASE 'database-name'3334Description35-----------36Change the workspace and database in the notebook.3738Arguments39---------40* ``<workspace-id>``: The ID of the workspace to delete.41* ``<workspace-name>``: The name of the workspace to delete.4243Remarks44-------45* If you want to specify a database in the current workspace,46the workspace name can be specified as ``@@CURRENT``.47* Specify the ``WITH DATABASE`` clause to select a default48database for the session.49* This command only works in a notebook session in the50Managed Service.5152Example53-------54The following command sets the workspace to ``examplews`` and55select 'dbname' as the default database::5657USE WORKSPACE 'examplews' WITH DATABASE 'dbname';5859"""60def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:61from singlestoredb.notebook import portal62if params['workspace'].get('current_workspace'):63if params.get('with_database'):64portal.default_database = params['with_database']65elif params.get('with_database'):66if params['workspace'].get('workspace_name'):67portal.connection = params['workspace']['workspace_name'], \68params['with_database']69else:70portal.connection = params['workspace']['workspace_id'], \71params['with_database']72elif params['workspace'].get('workspace_name'):73portal.workspace = params['workspace']['workspace_name']74else:75portal.workspace = params['workspace']['workspace_id']76return None777879UseWorkspaceHandler.register(overwrite=True)808182class ShowRegionsHandler(SQLHandler):83"""84SHOW REGIONS [ <like> ]85[ <order-by> ]86[ <limit> ];8788Description89-----------90Returns a list of all the valid regions for the user.9192Arguments93---------94* ``<pattern>``: A pattern similar to SQL LIKE clause.95Uses ``%`` as the wildcard character.9697Remarks98-------99* Use the ``LIKE`` clause to specify a pattern and return only the100regions that match the specified pattern.101* The ``LIMIT`` clause limits the number of results to the102specified number.103* Use the ``ORDER BY`` clause to sort the results by the specified104key. By default, the results are sorted in the ascending order.105106Example107-------108The following command returns a list of all the regions in the US109and sorts the results in ascending order by their ``Name``::110111SHOW REGIONS LIKE 'US%' ORDER BY Name;112113"""114115def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:116manager = get_workspace_manager()117118res = FusionSQLResult()119res.add_field('Name', result.STRING)120res.add_field('ID', result.STRING)121res.add_field('Provider', result.STRING)122123res.set_rows([(x.name, x.id, x.provider) for x in manager.regions])124125if params['like']:126res = res.like(Name=params['like'])127128return res.order_by(**params['order_by']).limit(params['limit'])129130131ShowRegionsHandler.register(overwrite=True)132133134class ShowWorkspaceGroupsHandler(SQLHandler):135"""136SHOW WORKSPACE GROUPS [ <like> ]137[ <extended> ] [ <order-by> ]138[ <limit> ];139140Description141-----------142Displays information on workspace groups.143144Arguments145---------146* ``<pattern>``: A pattern similar to SQL LIKE clause.147Uses ``%`` as the wildcard character.148149Remarks150-------151* Use the ``LIKE`` clause to specify a pattern and return only the152workspace groups that match the specified pattern.153* The ``LIMIT`` clause limits the number of results to the154specified number.155* Use the ``ORDER BY`` clause to sort the results by the specified156key. By default, the results are sorted in the ascending order.157* To return more information about the workspace groups, use the158``EXTENDED`` clause.159160Example161-------162The following command displays a list of workspace groups with names163that match the specified pattern::164165SHOW WORKSPACE GROUPS LIKE 'Marketing%' EXTENDED ORDER BY Name;166167See Also168--------169* ``SHOW WORKSPACES``170171"""172173def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:174manager = get_workspace_manager()175176res = FusionSQLResult()177res.add_field('Name', result.STRING)178res.add_field('ID', result.STRING)179res.add_field('Region', result.STRING)180res.add_field('FirewallRanges', result.JSON)181182if params['extended']:183res.add_field('CreatedAt', result.DATETIME)184res.add_field('TerminatedAt', result.DATETIME)185186def fields(x: Any) -> Any:187return (188x.name, x.id, x.region.name,189json.dumps(x.firewall_ranges),190dt_isoformat(x.created_at),191dt_isoformat(x.terminated_at),192)193else:194def fields(x: Any) -> Any:195return (x.name, x.id, x.region.name, json.dumps(x.firewall_ranges))196197res.set_rows([fields(x) for x in manager.workspace_groups])198199if params['like']:200res = res.like(Name=params['like'])201202return res.order_by(**params['order_by']).limit(params['limit'])203204205ShowWorkspaceGroupsHandler.register(overwrite=True)206207208class ShowWorkspacesHandler(SQLHandler):209"""210SHOW WORKSPACES [ in_group ]211[ <like> ] [ <extended> ]212[ <order-by> ] [ <limit> ];213214# Workspace group215in_group = IN GROUP { group_id | group_name }216217# ID of group218group_id = ID '<group-id>'219220# Name of group221group_name = '<group-name>'222223Description224-----------225Displays information on workspaces in a workspace group.226227Arguments228---------229* ``<group_id>``: The ID of the workspace group that contains230the workspace.231* ``<group_name>``: The name of the workspace group that232contains the workspace.233* ``<pattern>``: A pattern similar to SQL LIKE clause.234Uses ``%`` as the wildcard character.235236Remarks237-------238* The ``IN GROUP`` clause specifies the ID or the name of the239workspace group that contains the workspace.240* Use the ``LIKE`` clause to specify a pattern and return only241the workspaces that match the specified pattern.242* The ``LIMIT`` clause limits the number of results to the243specified number.244* Use the ``ORDER BY`` clause to sort the results by the245specified key. By default, the results are sorted in the246ascending order.247* To return more information about the workspaces, use the248``EXTENDED`` clause.249250Example251-------252The following command displays information on all the workspaces253in a workspace group named **wsg1** and sorts the results by254workspace name in the ascending order::255256SHOW WORKSPACES IN GROUP 'wsg1' EXTENDED ORDER BY Name;257258See Also259--------260* ``SHOW WORKSPACE GROUPS``261262"""263264def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:265res = FusionSQLResult()266res.add_field('Name', result.STRING)267res.add_field('ID', result.STRING)268res.add_field('Size', result.STRING)269res.add_field('State', result.STRING)270271workspace_group = get_workspace_group(params)272273if params['extended']:274res.add_field('Endpoint', result.STRING)275res.add_field('CreatedAt', result.DATETIME)276res.add_field('TerminatedAt', result.DATETIME)277278def fields(x: Any) -> Any:279return (280x.name, x.id, x.size, x.state,281x.endpoint, dt_isoformat(x.created_at),282dt_isoformat(x.terminated_at),283)284else:285def fields(x: Any) -> Any:286return (x.name, x.id, x.size, x.state)287288res.set_rows([fields(x) for x in workspace_group.workspaces])289290if params['like']:291res = res.like(Name=params['like'])292293return res.order_by(**params['order_by']).limit(params['limit'])294295296ShowWorkspacesHandler.register(overwrite=True)297298299class CreateWorkspaceGroupHandler(SQLHandler):300"""301CREATE WORKSPACE GROUP [ if_not_exists ] group_name302IN REGION { region_id | region_name }303[ with_password ]304[ expires_at ]305[ with_firewall_ranges ]306[ with_backup_bucket_kms_key_id ]307[ with_data_bucket_kms_key_id ]308[ with_smart_dr ]309[ allow_all_traffic ]310[ with_update_window ]311;312313# Only create workspace group if it doesn't exist already314if_not_exists = IF NOT EXISTS315316# Name of the workspace group317group_name = '<group-name>'318319# ID of region to create workspace group in320region_id = ID '<region-id>'321322# Name of region to create workspace group in323region_name = '<region-name>'324325# Admin password326with_password = WITH PASSWORD '<password>'327328# Datetime or interval for expiration date/time of workspace group329expires_at = EXPIRES AT '<iso-datetime-or-interval>'330331# Incoming IP ranges332with_firewall_ranges = WITH FIREWALL RANGES '<ip-range>',...333334# Backup bucket key335with_backup_bucket_kms_key_id = WITH BACKUP BUCKET KMS KEY ID '<key-id>'336337# Data bucket key338with_data_bucket_kms_key_id = WITH DATA BUCKET KMS KEY ID '<key-id>'339340# Smart DR341with_smart_dr = WITH SMART DR342343# Allow all incoming traffic344allow_all_traffic = ALLOW ALL TRAFFIC345346# Update window347with_update_window = WITH UPDATE WINDOW '<day>:<hour>'348349Description350-----------351Creates a workspace group.352353Arguments354---------355* ``<group-name>``: The name of the workspace group.356* ``<region_id>`` or ``<region_name>``: The ID or the name of the region357in which the new workspace group is created.358* ``<password>``: The admin password of the workspace group.359The password must contain:360- At least 8 characters361- At least one uppercase character362- At least one lowercase character363- At least one number or special character364* ``<expiry_time>``: The timestamp of when the workspace group terminates.365Expiration time can be specified as a timestamp or duration.366* ``<ip_range>``: A list of allowed IP addresses or CIDR ranges.367* ``<backup_key_id>``: The KMS key ID associated with the backup bucket.368* ``<data_key_id>``: The KMS key ID associated with the data bucket.369* ``<day>:<hour>``: The day of the week (0-6) and the hour of the day370(0-23) when the engine updates are applied to the workspace group.371372Remarks373-------374* Specify the ``IF NOT EXISTS`` clause to create a new workspace group only375if a workspace group with the specified name does not exist.376* If the ``WITH BACKUP BUCKET KMS KEY ID '<backup_key_id>'`` clause is377specified, Customer-Managed Encryption Keys (CMEK) encryption is enabled378for the data bucket of the workspace group.379* If the ``WITH DATA BUCKET KMS KEY ID '<data_key_id>'`` clause is specified,380CMEK encryption for the data bucket and Amazon Elastic Block Store (EBS)381volumes of the workspace group is enabled.382* To enable Smart Disaster Recovery (SmartDR) for the workspace group, specify383the WITH SMART DR clause. Refer to Smart Disaster Recovery (DR):384SmartDR for more information.385* To allow incoming traffic from any IP address, use the ``ALLOW ALL TRAFFIC``386clause.387388Examples389--------390The following command creates a workspace group named wsg1 in the391US East 2 (Ohio) region::392393CREATE WORKSPACE GROUP 'wsg1' IN REGION 'US East 2 (Ohio)';394395The following command specifies additional workspace group configuration396options::397398CREATE WORKSPACE GROUP 'wsg1'399IN REGION ID '93b61160-0000-1000-9000-977b8e2e3ee5'400WITH FIREWALL RANGES '0.0.0.0/0';401402See Also403--------404* ``SHOW WORKSPACE GROUPS``405406"""407408def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:409manager = get_workspace_manager()410411# Only create if one doesn't exist412if params['if_not_exists']:413try:414get_workspace_group(params)415return None416except (ValueError, KeyError):417pass418419# Get region ID420if params['region_name']:421regs = [x for x in manager.regions if x.name == params['region_name']]422if not regs:423raise KeyError(f'no region found with name "{params["region_name"]}"')424if len(regs) > 1:425raise ValueError(426f'multiple regions found with the name "{params["region_name"]}"',427)428region_id = regs[0].id429else:430region_id = params['region_id']431432with_update_window = None433if params['with_update_window']:434day, hour = params['with_update_window'].split(':', 1)435with_update_window = dict(day=int(day), hour=int(hour))436437manager.create_workspace_group(438params['group_name'],439region=region_id,440admin_password=params['with_password'],441expires_at=params['expires_at'],442firewall_ranges=params['with_firewall_ranges'],443backup_bucket_kms_key_id=params['with_backup_bucket_kms_key_id'],444data_bucket_kms_key_id=params['with_data_bucket_kms_key_id'],445smart_dr=params['with_smart_dr'],446allow_all_traffic=params['allow_all_traffic'],447update_window=with_update_window,448)449450return None451452453CreateWorkspaceGroupHandler.register(overwrite=True)454455456class CreateWorkspaceHandler(SQLHandler):457"""458CREATE WORKSPACE [ if_not_exists ] workspace_name459[ in_group ]460WITH SIZE size461[ auto_suspend ]462[ enable_kai ]463[ with_cache_config ]464[ wait_on_active ];465466# Create workspace in workspace group467in_group = IN GROUP { group_id | group_name }468469# Only run command if workspace doesn't already exist470if_not_exists = IF NOT EXISTS471472# Name of the workspace473workspace_name = '<workspace-name>'474475# ID of the group to create workspace in476group_id = ID '<group-id>'477478# Name of the group to create workspace in479group_name = '<group-name>'480481# Runtime size482size = '<size>'483484# Auto-suspend485auto_suspend = AUTO SUSPEND AFTER suspend_after_value suspend_after_units suspend_type486suspend_after_value = <integer>487suspend_after_units = { SECONDS | MINUTES | HOURS | DAYS }488suspend_type = WITH TYPE { IDLE | SCHEDULED | DISABLED }489490# Enable Kai491enable_kai = ENABLE KAI492493# Cache config494with_cache_config = WITH CACHE CONFIG <integer>495496# Wait for workspace to be active before continuing497wait_on_active = WAIT ON ACTIVE498499Description500-----------501Creates a new workspace. Refer to502`Creating and Using Workspaces <https://docs.singlestore.com/cloud/getting-started-with-singlestore-helios/about-workspaces/creating-and-using-workspaces/>`_503for more information.504505Arguments506---------507* ``<workspace_name>``: The name of the workspace.508* ``<group_id>`` or ``<group_name>``: The ID or name of the workspace group509in which the workspace is created.510* ``<workspace_size>``: The size of the workspace in workspace size notation,511for example "S-1".512* ``<suspend_time>``: The time (in given units) after which the workspace is513suspended, according to the specified auto-suspend type.514* ``<multiplier>``: The multiplier for the persistent cache associated with515the workspace.516517Remarks518-------519* Use the ``IF NOT EXISTS`` clause to create a new workspace only if a workspace520with the specified name does not exist.521* If the ``WITH CACHE CONFIG <multiplier>`` clause is specified, the cache522configuration multiplier is enabled for the workspace. It can have the523following values: 1, 2, or 4.524* The ``WAIT ON ACTIVE`` clause indicates that the execution is paused until this525workspace is in ACTIVE state.526* Specify the ``ENABLE KAI`` clause to enable SingleStore Kai and the MongoDB®527API for the workspace.528529Example530-------531The following command creates a workspace named **examplews** in a workspace532group named **wsg1**::533534CREATE WORKSPACE 'examplews' IN GROUP 'wsgroup1'535WITH SIZE 'S-00' WAIT ON ACTIVE;536537See Also538--------539* ``CREATE WORKSPACE GROUP``540541""" # noqa: E501542543def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:544workspace_group = get_workspace_group(params)545546# Only create if one doesn't exist547if params['if_not_exists']:548try:549workspace_group.workspaces[params['workspace_name']]550return None551except KeyError:552pass553554auto_suspend = None555if params['auto_suspend']:556mult = dict(557SECONDS=1,558MINUTES=60,559HOURS=60*60,560DAYS=60*60*24,561)562val = params['auto_suspend'][0]['suspend_after_value']563val = val * mult[params['auto_suspend'][1]['suspend_after_units'].upper()]564auto_suspend = dict(565suspend_after_seconds=val,566suspend_type=params['auto_suspend'][2]['suspend_type'].upper(),567)568569workspace_group.create_workspace(570params['workspace_name'],571size=params['size'],572auto_suspend=auto_suspend,573enable_kai=params['enable_kai'],574cache_config=params['with_cache_config'],575wait_on_active=params['wait_on_active'],576)577578return None579580581CreateWorkspaceHandler.register(overwrite=True)582583584class SuspendWorkspaceHandler(SQLHandler):585"""586SUSPEND WORKSPACE workspace587[ in_group ]588[ wait_on_suspended ];589590# Workspace591workspace = { workspace_id | workspace_name }592593# ID of workspace594workspace_id = ID '<workspace-id>'595596# Name of workspace597workspace_name = '<workspace-name>'598599# Workspace group600in_group = IN GROUP { group_id | group_name }601602# ID of workspace group603group_id = ID '<group-id>'604605# Name of workspace group606group_name = '<group-name>'607608# Wait for workspace to be suspended before continuing609wait_on_suspended = WAIT ON SUSPENDED610611Description612-----------613Suspends a workspace.614615Refer to `Manage Workspaces <https://docs.singlestore.com/cloud/user-and-workspace-administration/manage-organizations/manage-workspaces/>`_616for more information.617618Arguments619---------620* ``<workspace-id>``: The ID of the workspace to suspend.621* ``<workspace-name>``: The name of the workspace to suspend.622* ``<group-id>``: The ID of the workspace group that contains623the workspace.624* ``<group-name>``: The name of the workspace group that625contains the workspace.626627Remarks628-------629* Use the ``WAIT ON SUSPENDED`` clause to pause query execution630until the workspace is in the ``SUSPENDED`` state.631632Example633-------634The following example suspends a workspace named examplews in635a workspace group named **wsg1**::636637SUSPEND WORKSPACE 'examplews' IN GROUP 'wsg1';638639See Also640--------641* ``RESUME WORKSPACE``642643""" # noqa: E501644645def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:646ws = get_workspace(params)647ws.suspend(wait_on_suspended=params['wait_on_suspended'])648return None649650651SuspendWorkspaceHandler.register(overwrite=True)652653654class ResumeWorkspaceHandler(SQLHandler):655"""656RESUME WORKSPACE workspace657[ in_group ]658[ disable_auto_suspend ]659[ wait_on_resumed ];660661# Workspace662workspace = { workspace_id | workspace_name }663664# ID of workspace665workspace_id = ID '<workspace-id>'666667# Name of workspace668workspace_name = '<workspace-name>'669670# Workspace group671in_group = IN GROUP { group_id | group_name }672673# ID of workspace group674group_id = ID '<group-id>'675676# Name of workspace group677group_name = '<group-name>'678679# Disable auto-suspend680disable_auto_suspend = DISABLE AUTO SUSPEND681682# Wait for workspace to be resumed before continuing683wait_on_resumed = WAIT ON RESUMED684685Description686-----------687Resumes a workspace.688689Refer to `Manage Workspaces <https://docs.singlestore.com/cloud/user-and-workspace-administration/manage-organizations/manage-workspaces/>`_690for more information.691692Arguments693---------694* ``<workspace-id>``: The ID of the workspace to resume.695* ``<workspace-name>``: The name of the workspace to resume.696* ``<group_id>``: The ID of the workspace group that contains697the workspace.698* ``<group_name>``: The name of the workspace group that699contains the workspace.700701Remarks702-------703* Use the ``IN GROUP`` clause to specify the ID or name of the704workspace group that contains the workspace to resume.705* Use the ``WAIT ON RESUMED`` clause to pause query execution706until the workspace is in the ``RESUMED`` state.707* Specify the ``DISABLE AUTO SUSPEND`` clause to disable708auto-suspend for the resumed workspace.709710Example711-------712The following example resumes a workspace with the specified ID713in a workspace group named **wsg1**::714715RESUME WORKSPACE ID '93b61160-0000-1000-9000-977b8e2e3ee5'716IN GROUP 'wsg1';717718""" # noqa: E501719720def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:721ws = get_workspace(params)722ws.resume(723wait_on_resumed=params['wait_on_resumed'],724disable_auto_suspend=params['disable_auto_suspend'],725)726return None727728729ResumeWorkspaceHandler.register(overwrite=True)730731732class DropWorkspaceGroupHandler(SQLHandler):733"""734DROP WORKSPACE GROUP [ if_exists ]735group736[ wait_on_terminated ]737[ force ];738739# Only run command if the workspace group exists740if_exists = IF EXISTS741742# Workspace group743group = { group_id | group_name }744745# ID of the workspace group to delete746group_id = ID '<group-id>'747748# Name of the workspace group to delete749group_name = '<group-name>'750751# Wait for termination to complete before continuing752wait_on_terminated = WAIT ON TERMINATED753754# Should the workspace group be terminated even if it has workspaces?755force = FORCE756757Description758-----------759Deletes the specified workspace group.760761Arguments762---------763* ``<group_id>``: The ID of the workspace group to delete.764* ``<group_name>``: The name of the workspace group to delete.765766Remarks767-------768* Specify the ``IF EXISTS`` clause to attempt the delete operation769only if a workspace group with the specified ID or name exists.770* Use the ``WAIT ON TERMINATED`` clause to pause query execution until771the workspace group is in the ``TERMINATED`` state.772* If the ``FORCE`` clause is specified, the workspace group is773terminated even if it contains workspaces.774775Example776-------777The following command deletes a workspace group named **wsg1** even778if it contains workspaces::779780DROP WORKSPACE GROUP 'wsg1' FORCE;781782See Also783--------784* ``DROP WORKSPACE``785786"""787788def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:789try:790workspace_group = get_workspace_group(params)791if workspace_group.terminated_at is not None:792raise KeyError('workspace group is alread terminated')793workspace_group.terminate(794wait_on_terminated=params['wait_on_terminated'],795force=params['force'],796)797798except KeyError:799if not params['if_exists']:800raise801802return None803804805DropWorkspaceGroupHandler.register(overwrite=True)806807808class DropWorkspaceHandler(SQLHandler):809"""810DROP WORKSPACE [ if_exists ]811workspace812[ in_group ]813[ wait_on_terminated ];814815# Only drop workspace if it exists816if_exists = IF EXISTS817818# Workspace819workspace = { workspace_id | workspace_name }820821# ID of workspace822workspace_id = ID '<workspace-id>'823824# Name of workspace825workspace_name = '<workspace-name>'826827# Workspace group828in_group = IN GROUP { group_id | group_name }829830# ID of workspace group831group_id = ID '<group-id>'832833# Name of workspace group834group_name = '<group-name>'835836# Wait for workspace to be terminated before continuing837wait_on_terminated = WAIT ON TERMINATED838839Description840-----------841Deletes a workspace.842843Arguments844---------845* ``<workspace-id>``: The ID of the workspace to delete.846* ``<workspace-name>``: The name of the workspace to delete.847* ``<group_id>``: The ID of the workspace group that contains848the workspace.849* ``<group_name>``: The name of the workspace group that850contains the workspace.851852Remarks853-------854* Specify the ``IF EXISTS`` clause to attempt the delete operation855only if a workspace with the specified ID or name exists.856* Use the ``IN GROUP`` clause to specify the ID or name of the workspace857group that contains the workspace to delete.858* Use the ``WAIT ON TERMINATED`` clause to pause query execution until859the workspace is in the ``TERMINATED`` state.860* All databases attached to the workspace are detached when the861workspace is deleted (terminated).862863Example864-------865The following example deletes a workspace named **examplews** in866a workspace group **wsg1**::867868DROP WORKSPACE IF EXISTS 'examplews' IN GROUP 'wsg1';869870See Also871--------872* ``DROP WORKSPACE GROUP``873874"""875876def run(self, params: Dict[str, Any]) -> Optional[FusionSQLResult]:877try:878ws = get_workspace(params)879if ws.terminated_at is not None:880raise KeyError('workspace is already terminated')881ws.terminate(wait_on_terminated=params['wait_on_terminated'])882883except KeyError:884if not params['if_exists']:885raise886887return None888889890DropWorkspaceHandler.register(overwrite=True)891892893