{ "cells": [ { "cell_type": "markdown", "id": "6d924ae8-e462-4c47-86d9-8560ecedf78f", "metadata": {}, "source": [ "# Coder management" ] }, { "cell_type": "code", "execution_count": null, "id": "bb33cb2d-8256-4d65-9442-153a5eb920a6", "metadata": {}, "outputs": [], "source": [ "import csv\n", "import json" ] }, { "cell_type": "code", "execution_count": null, "id": "991fc1f2-61d2-443f-901b-df6acf094287", "metadata": {}, "outputs": [], "source": [ "cluster_name='inf110'\n", "proxyJump='ubuntu@137.194.210.143'" ] }, { "cell_type": "code", "execution_count": null, "id": "ae9a0955-2a17-499a-827b-8dfe8be56d65", "metadata": {}, "outputs": [], "source": [ "token,=!source openrc && echo $CODER_TOKEN" ] }, { "cell_type": "code", "execution_count": null, "id": "904c792f-2229-44cb-a98b-7714af05e163", "metadata": {}, "outputs": [], "source": [ "!https GET \"tp-inf110.r2.enst.fr/api/v2/buildinfo\" | jq '{ coder_version: .version, coder_url: .dashboard_url }'" ] }, { "cell_type": "markdown", "id": "463b7432-7861-44f6-8a42-36f453bf0a24", "metadata": {}, "source": [ "To retrieve the ID of the default organization, we can list the organizations of the admin user." ] }, { "cell_type": "code", "execution_count": null, "id": "3d2d2fe7-c20b-4e7d-a5da-e0c8021cb6f8", "metadata": { "scrolled": true }, "outputs": [], "source": [ "user=\"Zimmi48\"\n", "organization_id,=!https GET \"tp-inf110.r2.enst.fr/api/v2/users/{user}/organizations\" \"Coder-Session-Token:{token}\" | jq -r .[0].id" ] }, { "cell_type": "markdown", "id": "e953643e-a910-4b1a-bead-70caf4e2ebdd", "metadata": { "jp-MarkdownHeadingCollapsed": true }, "source": [ "## Create organization\n", "\n", "An organization is always created by default, so there is no point in creating another one for now." ] }, { "cell_type": "code", "execution_count": null, "id": "eaf47ee5-f83c-4ebd-8828-a5c4c89db924", "metadata": { "scrolled": true }, "outputs": [], "source": [ "#name=\"INF110\"\n", "#!https POST \"tp-inf110.r2.enst.fr/api/v2/organizations\" \"Coder-Session-Token:{token}\" \"name={name}\"" ] }, { "cell_type": "markdown", "id": "5fe6ded1-6fa1-48b4-b4bd-fc8eee714816", "metadata": { "jp-MarkdownHeadingCollapsed": true }, "source": [ "## Delete organization" ] }, { "cell_type": "code", "execution_count": null, "id": "f5dbf307-3352-498a-a175-93ad6ccd1155", "metadata": { "scrolled": true }, "outputs": [], "source": [ "#name=\"INF110\"\n", "#!https DELETE \"tp-inf110.r2.enst.fr/api/v2/organizations/{name}\" \"Coder-Session-Token:{token}\"" ] }, { "cell_type": "markdown", "id": "19a552b0-8bf8-4cd8-86a1-135b7e2253ed", "metadata": {}, "source": [ "## Create workspace template\n", "\n", "If it doesn't exist yet. Otherwise, retrieve the template and template version IDs." ] }, { "cell_type": "code", "execution_count": null, "id": "b4db687e-3e52-448d-b4d7-8c812cdaa2f7", "metadata": {}, "outputs": [], "source": [ "template_id,template_version=!https GET \"tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/templates\" \"Coder-Session-Token:{token}\" | jq -r '.[0] | .id, .active_version_id'\n", "if template_id == 'null':\n", " # Upload template files\n", " file_hash=!cd inf110-workspace && zip \"template.zip\" \"main.tf\" \"README.md\" && https POST \"tp-inf110.r2.enst.fr/api/v2/files\" \"Coder-Session-Token:{token}\" \"Content-Type:application/zip\" < \"template.zip\" | jq -r '.hash'\n", " # Create template version\n", " version_name='INF110-2024'\n", " template_version=!https POST \"tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/templateversions\" \"Coder-Session-Token:{token}\" \"name={version_name}\" \"file_id={file_hash}\" \"storage_method=file\" \"provisioner=terraform\" | jq -r '.id'\n", " # Create template\n", " template_name='INF110'\n", " display_name='TP INF110'\n", " description=\"Espace de travail pour les TP d'INF110\"\n", " template_id=!https POST \"tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/templates\" \"Coder-Session-Token:{token}\" \"name={template_name}\" \"display_name={display_name}\" \"description={description}\" \"icon=/emojis/1f4d0.png\" \"template_version_id={template_version}\" | jq -r '.id'" ] }, { "cell_type": "markdown", "id": "e20dfefb-0359-421a-92c6-ab403de529dc", "metadata": {}, "source": [ "The query below can be used to check that the template creation was successful." ] }, { "cell_type": "code", "execution_count": null, "id": "1ae74b43-8065-4e5f-9976-e00a879d7c99", "metadata": { "scrolled": true }, "outputs": [], "source": [ "!https GET \"tp-inf110.r2.enst.fr/api/v2/templateversions/{template_version}\" \"Coder-Session-Token:{token}\" | jq '.job.status'" ] }, { "cell_type": "markdown", "id": "5d900272-a3ee-461c-9cdd-df08b17b1d2a", "metadata": {}, "source": [ "## Create users" ] }, { "cell_type": "code", "execution_count": null, "id": "9f05d75f-c300-49ca-9faf-19a1a24dd6d8", "metadata": {}, "outputs": [], "source": [ "def create_user(email,username):\n", " passwords=!pwgen -s 10 -1\n", " password=passwords[0]\n", " !echo \"{email},{username},{password}\" >> users.csv && \\\n", " https POST \"tp-inf110.r2.enst.fr/api/v2/users\" \"Coder-Session-Token:{token}\" \"email={email}\" \"login_type=password\" \"password={password}\" \"username={username}\"\n", "\n", "#create_user(\"jean.leneutre@telecom-paris.fr\", \"jleneutre\")" ] }, { "cell_type": "code", "execution_count": null, "id": "96595327-81a0-4a50-b090-a79d75a3134d", "metadata": { "scrolled": true }, "outputs": [], "source": [ "def create_student_accounts(csv_filename):\n", " with open(csv_filename, mode='r') as csv_file:\n", " csv_reader = csv.DictReader(csv_file)\n", " for student in csv_reader:\n", " create_user(student['mail'], student['login'])\n", "\n", "#create_student_accounts('inf110-2023-B.csv')\n", "#create_student_accounts('inf110-2023-D.csv')\n", "#create_student_accounts('MITRO202.csv')" ] }, { "cell_type": "markdown", "id": "9868d906-ca8c-4fda-ad96-ec00bea5fadc", "metadata": {}, "source": [ "## Delete users\n", "\n", "(Requires having deleted their workspaces first.)" ] }, { "cell_type": "code", "execution_count": null, "id": "30a56dca-7f8f-4839-94e7-36340f5f199f", "metadata": {}, "outputs": [], "source": [ "def delete_user(username):\n", " !https DELETE \"tp-inf110.r2.enst.fr/api/v2/users/{username}\" \"Coder-Session-Token:{token}\"\n", "\n", "#delete_user(\"bbinder\")" ] }, { "cell_type": "markdown", "id": "d6d20c1f-e231-488c-90ef-2daa811e89a2", "metadata": {}, "source": [ "## Create workspaces" ] }, { "cell_type": "code", "execution_count": null, "id": "80ff8f4e-fa5f-4308-a885-e3d382e2a171", "metadata": { "scrolled": true }, "outputs": [], "source": [ "def create_workspace(username, name='tp'):\n", " !https POST \"tp-inf110.r2.enst.fr/api/v2/organizations/{organization_id}/members/{username}/workspaces\" \"Coder-Session-Token:{token}\" \"name={name}\" \"template_id={template_id}\"\n", "\n", "#create_workspace(\"Zimmi48\")" ] }, { "cell_type": "markdown", "id": "d2cea793-4b57-4718-bd17-7cbda610d5bd", "metadata": {}, "source": [ "Test auto-scaling by creating many workspaces at once." ] }, { "cell_type": "code", "execution_count": null, "id": "e3d06f0c-3183-47e4-a63e-254a6c08612a", "metadata": { "scrolled": true }, "outputs": [], "source": [ "#for i in range(0,99):\n", "# create_workspace('Zimmi48', 'autoscaling-test-%d' % i)" ] }, { "cell_type": "code", "execution_count": null, "id": "11d58691-691b-41a8-bdbf-51d3b313b64f", "metadata": { "scrolled": true }, "outputs": [], "source": [ "def create_student_workspaces(csv_filename):\n", " with open(csv_filename, mode='r') as csv_file:\n", " csv_reader = csv.DictReader(csv_file)\n", " for student in csv_reader:\n", " create_workspace(student['login'])\n", "\n", "#create_student_workspaces('inf110-2023-B.csv')\n", "#create_student_workspaces('inf110-2023-D.csv')" ] }, { "cell_type": "markdown", "id": "1b405071-7dd5-4082-80f2-dbd60ea46262", "metadata": {}, "source": [ "## Start / stop all workspaces from a student group" ] }, { "cell_type": "code", "execution_count": null, "id": "e9be7a12-c858-4baf-9ac7-53827330798a", "metadata": {}, "outputs": [], "source": [ "def get_student_username_list(csv_filename):\n", " students = []\n", " with open(csv_filename, mode='r') as csv_file:\n", " csv_reader = csv.DictReader(csv_file)\n", " for student in csv_reader:\n", " students += [student['login']]\n", " return students\n", "\n", "#groupB = get_student_username_list('inf110-2023-B.csv')\n", "#groupD = get_student_username_list('inf110-2023-D.csv')" ] }, { "cell_type": "code", "execution_count": null, "id": "1640ad41-d82c-4519-88d3-ef2c2cfc2950", "metadata": {}, "outputs": [], "source": [ "len(groupB)" ] }, { "cell_type": "code", "execution_count": null, "id": "6b0b126a-b7e4-43c5-9aab-2c27931ebf91", "metadata": {}, "outputs": [], "source": [ "len(groupD)" ] }, { "cell_type": "code", "execution_count": null, "id": "b495de0f-d085-4db6-a8a4-9a022f345251", "metadata": { "scrolled": true }, "outputs": [], "source": [ "def stop_workspaces_from_group(group):\n", " workspaces = !https GET \"tp-inf110.r2.enst.fr/api/v2/workspaces\" \"Coder-Session-Token:{token}\" \"limit==100\" \"q==status:running\" -b | jq -cr '.workspaces | .[]'\n", " workspaces = list(map(json.loads, workspaces))\n", " for workspace in [workspace['id'] for workspace in workspaces if workspace['owner_name'] in group]:\n", " !https POST \"tp-inf110.r2.enst.fr/api/v2/workspaces/{workspace}/builds\" \"Coder-Session-Token:{token}\" \"transition=stop\"\n", "\n", "stop_workspaces_from_group(['Zimmi48'])" ] }, { "cell_type": "code", "execution_count": null, "id": "13f05a6e-f591-4bcd-9857-63f5a21f1028", "metadata": { "scrolled": true }, "outputs": [], "source": [ "def start_workspaces_from_group(group):\n", " workspaces = !https GET \"tp-inf110.r2.enst.fr/api/v2/workspaces\" \"Coder-Session-Token:{token}\" \"limit==100\" \"q==status:stopped\" -b | jq -cr '.workspaces | .[]'\n", " workspaces = list(map(json.loads, workspaces))\n", " for workspace in [workspace['id'] for workspace in workspaces if workspace['owner_name'] in group]:\n", " !https POST \"tp-inf110.r2.enst.fr/api/v2/workspaces/{workspace}/builds\" \"Coder-Session-Token:{token}\" \"transition=start\" \"template_version_id={template_version}\"\n", "\n", "start_workspaces_from_group(mitro)" ] }, { "cell_type": "markdown", "id": "e8e88940-8c7c-4b3c-8923-aaf0b813f524", "metadata": {}, "source": [ "## Delete all workspaces and users from a student group" ] }, { "cell_type": "code", "execution_count": null, "id": "5c22d1b3-923f-45fd-ac04-9f404e5002b6", "metadata": { "scrolled": true }, "outputs": [], "source": [ "def delete_workspaces_from_group(group):\n", " workspaces = !https GET \"tp-inf110.r2.enst.fr/api/v2/workspaces\" \"Coder-Session-Token:{token}\" \"limit==100\" \"q==status:stopped\" -b | jq -cr '.workspaces | .[]'\n", " workspaces = list(map(json.loads, workspaces))\n", " for workspace in [workspace['id'] for workspace in workspaces if workspace['owner_name'] in group]:\n", " !https POST \"tp-inf110.r2.enst.fr/api/v2/workspaces/{workspace}/builds\" \"Coder-Session-Token:{token}\" \"transition=delete\"\n", "\n", "delete_workspaces_from_group(['Zimmi48'])" ] }, { "cell_type": "code", "execution_count": null, "id": "eafaba21-fdf9-46e9-ba8f-bc1f25de96a4", "metadata": { "scrolled": true }, "outputs": [], "source": [ "def delete_students(group):\n", " for student in group:\n", " delete_user(student)\n", "\n", "delete_students(groupD)" ] }, { "cell_type": "markdown", "id": "12c57f21-23fe-4752-ad28-b05bd33ff08f", "metadata": { "jp-MarkdownHeadingCollapsed": true }, "source": [ "## Stop all unhealthy workspaces" ] }, { "cell_type": "code", "execution_count": null, "id": "73ce34f9-b0ad-4434-b448-6e2fdcfed6bd", "metadata": { "scrolled": true }, "outputs": [], "source": [ "def stop_unhealthy_workspaces():\n", " workspaces = !https GET \"tp-inf110.r2.enst.fr/api/v2/workspaces\" \"Coder-Session-Token:{token}\" \"limit==100\" \"q==status:running\" -b | jq -cr '.workspaces | .[]'\n", " workspaces = list(map(json.loads, workspaces))\n", " for workspace in [workspace for workspace in workspaces if not workspace['health']['healthy']]:\n", " !https POST \"tp-inf110.r2.enst.fr/api/v2/workspaces/{workspace['id']}/builds\" \"Coder-Session-Token:{token}\" \"transition=stop\"\n", "\n", "stop_unhealthy_workspaces()" ] }, { "cell_type": "markdown", "id": "acd9cb0e-9596-4091-ae35-82a34ff9f0b4", "metadata": {}, "source": [ "## Check health of running workspaces" ] }, { "cell_type": "code", "execution_count": null, "id": "6c4beb11-717a-4d89-b9a4-e9b033b3e6a4", "metadata": {}, "outputs": [], "source": [ "servers = {}" ] }, { "cell_type": "code", "execution_count": null, "id": "9bcdc633-0b28-46e4-aa54-97dc20f7d091", "metadata": {}, "outputs": [], "source": [ "healthy_workspaces = []\n", "unhealthy_workspaces = []\n", "\n", "workspaces = !https GET \"tp-inf110.r2.enst.fr/api/v2/workspaces\" \"Coder-Session-Token:{token}\" \"limit==100\" \"q==status:running\" -b | jq -cr '.workspaces | .[]'\n", "workspaces = list(map(json.loads, workspaces))\n", "for workspace in workspaces:\n", " if workspace['health']['healthy']:\n", " healthy_workspaces += [workspace['owner_name'].lower()]\n", " else:\n", " unhealthy_workspaces += [workspace['owner_name'].lower()]" ] }, { "cell_type": "code", "execution_count": null, "id": "ffb92e49-aa8b-4371-bade-a06104020e32", "metadata": {}, "outputs": [], "source": [ "print(\"Healthy workspaces: %d\" % len(healthy_workspaces))\n", "print(\"Unhealthy workspaces: %d\" % len(unhealthy_workspaces))" ] }, { "cell_type": "code", "execution_count": null, "id": "08d53bc7-fd70-4a7f-97d3-be5e737cdbaa", "metadata": {}, "outputs": [], "source": [ "volumes=!source openrc && openstack volume list --long -f json | jq -c '.[] | { id: .Name, name: .Properties.\"csi.storage.k8s.io/pvc/name\", status: .Status, location: .Name, attached_to: .\"Attached to\" } | select(.name != null) | select(.name | startswith(\"coder-pvc-\") and contains(\"-tp-inf110\")) | { name: .id, username: .name | split(\"-\") | .[2], status: .status, server_id: .attached_to[0].server_id, location: (\"/var/lib/kubelet/plugins/kubernetes.io/csi/pv/\" + .location + \"/globalmount\") }'\n", "volumes = list(map(json.loads, volumes))" ] }, { "cell_type": "code", "execution_count": null, "id": "c183542f-6abf-4fbe-8b64-386da87a2c95", "metadata": {}, "outputs": [], "source": [ "[volume for volume in volumes if volume['server_id'] is not None and not volume['username'] in healthy_workspaces]" ] }, { "cell_type": "code", "execution_count": null, "id": "db003235-11d9-4386-b8fe-944955e5e257", "metadata": {}, "outputs": [], "source": [ "for server in servers:\n", " servers[server]['users'] = []\n", "for volume in volumes:\n", " if volume['server_id'] is not None:\n", " if not volume['server_id'] in servers:\n", " ip,=!source openrc && openstack server show \"{volume['server_id']}\" -f json | jq -r '.addresses.\"{cluster_name}\"[0]'\n", " servers[volume['server_id']] = { 'ip': ip, 'users': [volume['username']] }\n", " else:\n", " servers[volume['server_id']]['users'] += [volume['username']]" ] }, { "cell_type": "code", "execution_count": null, "id": "8cee7a31-4e22-4ff4-9b06-5c6b014ddea8", "metadata": { "scrolled": true }, "outputs": [], "source": [ "for server in servers:\n", " print(\"Server: \" + servers[server]['ip'])\n", " for user in servers[server]['users']:\n", " print('User: ' + user, end='')\n", " if user in healthy_workspaces:\n", " print(' (healthy)')\n", " elif user in unhealthy_workspaces:\n", " print(' (unhealthy)')\n", " else:\n", " print(' (unknown)')\n", " print()\n", " print()" ] }, { "cell_type": "markdown", "id": "79725809-695a-4ab4-9e58-79bb747b8b05", "metadata": {}, "source": [ "## Clean workspaces" ] }, { "cell_type": "code", "execution_count": null, "id": "1a29e39d-2abb-4b2a-b663-3455f956b46d", "metadata": { "scrolled": true }, "outputs": [], "source": [ "def remove_lost_found():\n", " for volume in volumes:\n", " if volume['server_id'] is not None:\n", " machine_ip = servers[volume['server_id']]['ip']\n", " lost_found = volume['location'] + \"/lost+found\"\n", " command = \"set -xe; \"\n", " command += \"if [ -d \" + lost_found + \" ]; then rmdir \" + lost_found + \"; fi; \"\n", " !ssh -oStrictHostKeyChecking=no -J core@{public_ip} core@{machine_ip} sudo sh -c \"\\\"{command}\\\"\"\n", "\n", "remove_lost_found()" ] }, { "cell_type": "markdown", "id": "75661986-3bb2-42b0-abe6-1a5f18ea9615", "metadata": {}, "source": [ "## Copy / update files in workspace" ] }, { "cell_type": "code", "execution_count": null, "id": "13d7c970-4863-4fac-bb58-722bb9c27f34", "metadata": {}, "outputs": [], "source": [ "past_versions = {\n", " 'tp1.ipynb': [\n", " 'e6f11284e2ba7d4a01350a1f2c0ab208',\n", " 'c208201a6aaf72f102fbd2cf46b62e5b',\n", " ],\n", " 'tp2.mv': [\n", " 'b3b2a3e7a8dde6ed8848300158d89e5d',\n", " '60a26b6a7d07579f63f6054c24b87ea2',\n", " '190845731e26037dcbc7e3a74d239b1b',\n", " 'd010ae9ab0188b4cc77a3d5a45df2f1a',\n", " ],\n", " 'tp3.mv': [\n", " '91621d47a84e460e567dc51dfe1fcf45',\n", " ],\n", " 'tp4.mv': [\n", " 'a819a3d31373750cfcb8473a27cd5c3f',\n", " ],\n", " 'tp5.mv': [],\n", " 'settings.json': [\n", " 'aca81e70023acea605dd44e829d04339', # Autogenerated by coq-lsp\n", " '7378b13908523bfde3692518f61939cf',\n", " '2c2178abd6661d10ae492bf6fdc0454b',\n", " '350ac51d8fea466958d9986c96c84842',\n", " 'ded5f984f385aa61ef30e01fef43f3e4',\n", " ],\n", " \"_CoqProject\": [],\n", "}" ] }, { "cell_type": "code", "execution_count": null, "id": "876c1a46-9dd4-4f4c-8a4e-185dc361d5a1", "metadata": {}, "outputs": [], "source": [ "current_version = {\n", " 'tp1.ipynb': '009c42561f1853af966f95e9dcee9c24',\n", " 'tp2.mv': '78139922e6b6a531e7e70acc98c1019c',\n", " 'tp3.mv': '0a0253e4825146da6f08eff073b10444',\n", " 'tp4.mv': '203ce16afa53d60cfd8390fb3fb7d898',\n", " 'tp5.mv': '3ec8626aa544cb075af1f78719a1fb48',\n", " 'settings.json': '3ddc1036917034cec13503b74d3b051e',\n", " '_CoqProject': 'b1d60da8dd98106e00006d99428d1403',\n", "}" ] }, { "cell_type": "code", "execution_count": null, "id": "a8f0c5e5-f995-43bd-9cb8-08099e6ea8fa", "metadata": { "scrolled": true }, "outputs": [], "source": [ "def copy_update_file(filename):\n", " file_hash, = !md5sum {\"../INF110/\" + filename} | cut -f1 -d' '\n", " if file_hash != current_version[filename]:\n", " print(\"Hash of \" + filename + \" has changed. Please update past_versions and current_version. New hash is \" + file_hash)\n", " return\n", " \n", " for server in servers:\n", " !scp -o StrictHostKeyChecking=no -J {proxyJump} {\"../INF110/\" + filename} core@{servers[server]['ip']}:/tmp\n", " \n", " for volume in volumes:\n", " if volume['server_id'] is not None:\n", " machine_ip = servers[volume['server_id']]['ip']\n", " !ssh -J {proxyJump} core@{machine_ip} sudo cp -nv /tmp/{filename} {volume['location']}\n", " file_hash, = !ssh -J {proxyJump} core@{machine_ip} sudo md5sum {volume['location'] + \"/\" + filename} | cut -f1 -d' '\n", " if file_hash in past_versions[filename]:\n", " !ssh -J {proxyJump} core@{machine_ip} sudo cp -v /tmp/{filename} {volume['location']}\n", " !ssh -J {proxyJump} core@{machine_ip} sudo chmod go+rw {volume['location'] + \"/\" + filename}\n", "\n", "copy_update_file(\"tp1.ipynb\")\n", "copy_update_file(\"_CoqProject\")\n", "copy_update_file(\"tp2.mv\")\n", "copy_update_file(\"tp3.mv\")\n", "copy_update_file(\"tp4.mv\")\n", "copy_update_file(\"tp5.mv\")" ] }, { "cell_type": "code", "execution_count": null, "id": "13608dc8-78f6-41d3-9cc3-f96c603a9437", "metadata": { "scrolled": true }, "outputs": [], "source": [ "def copy_read_only_file(filename):\n", " for server in servers:\n", " !scp -J {proxyJump} {\"../INF110/\" + filename} core@{servers[server]['ip']}:/tmp\n", " \n", " for volume in volumes:\n", " if volume['server_id'] is not None:\n", " machine_ip = servers[volume['server_id']]['ip']\n", " !ssh -J {proxyJump} core@{machine_ip} sudo cp -v /tmp/{filename} {volume['location']}\n", "\n", "copy_read_only_file('README.md')\n", "copy_read_only_file(\"tp1-mysterious-tm.png\")" ] }, { "cell_type": "code", "execution_count": null, "id": "4cc929db-08fe-4b7b-9293-d86b7124f7cf", "metadata": { "scrolled": true }, "outputs": [], "source": [ "def copy_vscode_settings():\n", " file_hash, = !md5sum {\"../INF110/.vscode/settings.json\"} | cut -f1 -d' '\n", " if file_hash != current_version['settings.json']:\n", " print(\"Hash of settings.json has changed. Please update past_versions and current_version. New hash is \" + file_hash)\n", " return\n", "\n", " for server in servers:\n", " !scp -J {proxyJump} -o StrictHostKeyChecking=no \"../INF110/.vscode/settings.json\" core@{servers[server]['ip']}:/tmp\n", " \n", " for volume in volumes:\n", " if volume['server_id'] is not None:\n", " machine_ip = servers[volume['server_id']]['ip']\n", " vscode = volume['location'] + \"/.vscode\"\n", " command = \"set -xe; \"\n", " command += \"if [ ! -d \" + vscode + \" ]; then mkdir \" + vscode + \"; fi; \"\n", " command += \"cp -nv /tmp/settings.json \" + vscode + \"/settings.json; \"\n", " !ssh -J {proxyJump} core@{machine_ip} sudo sh -c \"\\\"{command}\\\"\"\n", " file_hash, = !ssh -J {proxyJump} core@{machine_ip} sudo md5sum {vscode + \"/settings.json\"} | cut -f1 -d' '\n", " if file_hash in past_versions['settings.json']:\n", " !ssh -J {proxyJump} core@{machine_ip} sudo cp -v /tmp/settings.json {vscode}\n", " !ssh -J {proxyJump} core@{machine_ip} sudo chmod go+rw {vscode + \"/settings.json\"}\n", "\n", "copy_vscode_settings()" ] }, { "cell_type": "markdown", "id": "55bd39c3-794e-43f2-8bf0-342361d61b64", "metadata": {}, "source": [ "## Save snapshots of modified files" ] }, { "cell_type": "code", "execution_count": null, "id": "db436f12-abb7-4b48-9e66-2f6ada19bdff", "metadata": {}, "outputs": [], "source": [ "def snapshot(filename, snapshot_name):\n", " !mkdir -p {snapshot_name}\n", " for volume in volumes:\n", " if volume['server_id'] is not None:\n", " machine_ip = servers[volume['server_id']]['ip']\n", " file_hash, = !ssh -o StrictHostKeyChecking=no -J {proxyJump} core@{machine_ip} sudo md5sum {volume['location'] + \"/\" + filename} | cut -f1 -d' '\n", " if not file_hash == current_version[filename] and not file_hash in past_versions[filename]:\n", " snapshot_filename = \"/tmp/\" + volume['username'] + \"-\" + snapshot_name + \"-\" + filename\n", " !ssh -J {proxyJump} core@{machine_ip} sudo cp {volume['location'] + \"/\" + filename} {snapshot_filename}\n", " !scp -J {proxyJump} core@{machine_ip}:{snapshot_filename} {snapshot_name + \"/\" + volume['username'] + \"_\" + filename}\n", "\n", "#snapshot('tp1.ipynb', '2023-12-04-post-tp')\n", "#snapshot('tp2.mv', '2023-12-13-post-tp')\n", "#snapshot('tp3.mv', '2023-12-13-post-tp')\n", "#snapshot('tp4.mv', '2024-01-12-post-tp')\n", "#snapshot('tp5.mv', '2024-01-22-post-tp')" ] }, { "cell_type": "code", "execution_count": null, "id": "35af9e82-e79c-4ad9-a843-3ebaab9f4d62", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 5 }