python-chi 1.0.2__tar.gz → 1.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. python_chi-1.0.4/ChangeLog +7 -0
  2. {python_chi-1.0.2 → python_chi-1.0.4}/PKG-INFO +1 -1
  3. {python_chi-1.0.2 → python_chi-1.0.4}/chi/container.py +2 -1
  4. {python_chi-1.0.2 → python_chi-1.0.4}/chi/hardware.py +3 -1
  5. {python_chi-1.0.2 → python_chi-1.0.4}/chi/network.py +21 -0
  6. {python_chi-1.0.2 → python_chi-1.0.4}/chi/server.py +11 -2
  7. {python_chi-1.0.2 → python_chi-1.0.4}/chi/util.py +11 -0
  8. python_chi-1.0.4/docs/examples.rst +92 -0
  9. {python_chi-1.0.2 → python_chi-1.0.4}/docs/index.rst +5 -1
  10. {python_chi-1.0.2 → python_chi-1.0.4}/python_chi.egg-info/PKG-INFO +1 -1
  11. {python_chi-1.0.2 → python_chi-1.0.4}/python_chi.egg-info/SOURCES.txt +1 -0
  12. python_chi-1.0.4/python_chi.egg-info/pbr.json +1 -0
  13. {python_chi-1.0.2 → python_chi-1.0.4}/setup.py +1 -1
  14. python_chi-1.0.2/ChangeLog +0 -7
  15. python_chi-1.0.2/python_chi.egg-info/pbr.json +0 -1
  16. {python_chi-1.0.2 → python_chi-1.0.4}/.github/CODEOWNERS +0 -0
  17. {python_chi-1.0.2 → python_chi-1.0.4}/.github/workflows/pypi-publish.yml +0 -0
  18. {python_chi-1.0.2 → python_chi-1.0.4}/.github/workflows/test.yml +0 -0
  19. {python_chi-1.0.2 → python_chi-1.0.4}/.mailmap +0 -0
  20. {python_chi-1.0.2 → python_chi-1.0.4}/.readthedocs.yml +0 -0
  21. {python_chi-1.0.2 → python_chi-1.0.4}/AUTHORS +0 -0
  22. {python_chi-1.0.2 → python_chi-1.0.4}/DEVELOPMENT.rst +0 -0
  23. {python_chi-1.0.2 → python_chi-1.0.4}/LICENSE +0 -0
  24. {python_chi-1.0.2 → python_chi-1.0.4}/Makefile +0 -0
  25. {python_chi-1.0.2 → python_chi-1.0.4}/README.rst +0 -0
  26. {python_chi-1.0.2 → python_chi-1.0.4}/chi/__init__.py +0 -0
  27. {python_chi-1.0.2 → python_chi-1.0.4}/chi/clients.py +0 -0
  28. {python_chi-1.0.2 → python_chi-1.0.4}/chi/context.py +0 -0
  29. {python_chi-1.0.2 → python_chi-1.0.4}/chi/exception.py +0 -0
  30. {python_chi-1.0.2 → python_chi-1.0.4}/chi/image.py +0 -0
  31. {python_chi-1.0.2 → python_chi-1.0.4}/chi/jupyterhub.py +0 -0
  32. {python_chi-1.0.2 → python_chi-1.0.4}/chi/keypair.py +0 -0
  33. {python_chi-1.0.2 → python_chi-1.0.4}/chi/lease.py +0 -0
  34. {python_chi-1.0.2 → python_chi-1.0.4}/chi/magic.py +0 -0
  35. {python_chi-1.0.2 → python_chi-1.0.4}/chi/share.py +0 -0
  36. {python_chi-1.0.2 → python_chi-1.0.4}/chi/ssh.py +0 -0
  37. {python_chi-1.0.2 → python_chi-1.0.4}/chi/storage.py +0 -0
  38. {python_chi-1.0.2 → python_chi-1.0.4}/docs/__init__.py +0 -0
  39. {python_chi-1.0.2 → python_chi-1.0.4}/docs/conf.py +0 -0
  40. {python_chi-1.0.2 → python_chi-1.0.4}/docs/generate_notebook.py +0 -0
  41. {python_chi-1.0.2 → python_chi-1.0.4}/docs/modules/clients.rst +0 -0
  42. {python_chi-1.0.2 → python_chi-1.0.4}/docs/modules/container.rst +0 -0
  43. {python_chi-1.0.2 → python_chi-1.0.4}/docs/modules/context.rst +0 -0
  44. {python_chi-1.0.2 → python_chi-1.0.4}/docs/modules/exception.rst +0 -0
  45. {python_chi-1.0.2 → python_chi-1.0.4}/docs/modules/hardware.rst +0 -0
  46. {python_chi-1.0.2 → python_chi-1.0.4}/docs/modules/image.rst +0 -0
  47. {python_chi-1.0.2 → python_chi-1.0.4}/docs/modules/lease.rst +0 -0
  48. {python_chi-1.0.2 → python_chi-1.0.4}/docs/modules/magic.rst +0 -0
  49. {python_chi-1.0.2 → python_chi-1.0.4}/docs/modules/network.rst +0 -0
  50. {python_chi-1.0.2 → python_chi-1.0.4}/docs/modules/server.rst +0 -0
  51. {python_chi-1.0.2 → python_chi-1.0.4}/docs/modules/share.rst +0 -0
  52. {python_chi-1.0.2 → python_chi-1.0.4}/docs/modules/ssh.rst +0 -0
  53. {python_chi-1.0.2 → python_chi-1.0.4}/docs/modules/storage.rst +0 -0
  54. {python_chi-1.0.2 → python_chi-1.0.4}/docs/requirements.txt +0 -0
  55. {python_chi-1.0.2 → python_chi-1.0.4}/python_chi.egg-info/dependency_links.txt +0 -0
  56. {python_chi-1.0.2 → python_chi-1.0.4}/python_chi.egg-info/not-zip-safe +0 -0
  57. {python_chi-1.0.2 → python_chi-1.0.4}/python_chi.egg-info/requires.txt +0 -0
  58. {python_chi-1.0.2 → python_chi-1.0.4}/python_chi.egg-info/top_level.txt +0 -0
  59. {python_chi-1.0.2 → python_chi-1.0.4}/requirements.txt +0 -0
  60. {python_chi-1.0.2 → python_chi-1.0.4}/setup.cfg +0 -0
  61. {python_chi-1.0.2 → python_chi-1.0.4}/test-requirements.txt +0 -0
  62. {python_chi-1.0.2 → python_chi-1.0.4}/tests/__init__.py +0 -0
  63. {python_chi-1.0.2 → python_chi-1.0.4}/tests/test_container.py +0 -0
  64. {python_chi-1.0.2 → python_chi-1.0.4}/tests/test_context.py +0 -0
  65. {python_chi-1.0.2 → python_chi-1.0.4}/tests/test_lease.py +0 -0
  66. {python_chi-1.0.2 → python_chi-1.0.4}/tests/test_network.py +0 -0
  67. {python_chi-1.0.2 → python_chi-1.0.4}/tests/test_server.py +0 -0
  68. {python_chi-1.0.2 → python_chi-1.0.4}/tests/test_share.py +0 -0
  69. {python_chi-1.0.2 → python_chi-1.0.4}/tests/test_ssh.py +0 -0
  70. {python_chi-1.0.2 → python_chi-1.0.4}/tox.ini +0 -0
@@ -0,0 +1,7 @@
1
+ CHANGES
2
+ =======
3
+
4
+ v1.0.4
5
+ ------
6
+
7
+ * Fix container download python version compatibility
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-chi
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: Helper library for Chameleon Infrastructure (CHI) testbed
5
5
  Home-page: https://www.chameleoncloud.org
6
6
  Author: University of Chicago
@@ -515,7 +515,8 @@ def download(container_ref: "str", source: "str", dest: "str"):
515
515
  res = zun().containers.get_archive(container_ref, source)
516
516
  fd = io.BytesIO(res["data"])
517
517
  with tarfile.open(fileobj=fd, mode="r") as tar:
518
- tar.extractall(dest, filter="fully_trusted")
518
+ tar.extraction_filter = (lambda member, path: member)
519
+ tar.extractall(dest)
519
520
 
520
521
 
521
522
  def wait_for_active(container_ref: "str", timeout: int = (60 * 2)) -> "Container":
@@ -53,7 +53,7 @@ class Node:
53
53
 
54
54
  def get_host_id(items, target_uid):
55
55
  for item in items:
56
- if item.get("uid") == target_uid:
56
+ if item.get("uid") == target_uid or item.get("hypervisor_hostname") == target_uid:
57
57
  return item["id"]
58
58
  return None
59
59
 
@@ -62,6 +62,8 @@ class Node:
62
62
  # Get allocation for this specific host
63
63
  host_id = get_host_id(blazarclient.host.list(), self.uid)
64
64
 
65
+ if not host_id:
66
+ raise exception.ServiceError(f"Host for {self.uid} not found in Blazar")
65
67
  allocation = blazarclient.host.get_allocation(host_id)
66
68
 
67
69
  now = datetime.now(timezone.utc)
@@ -187,6 +187,13 @@ def list_networks() -> "list[dict]":
187
187
  return neutron().list_networks()["networks"]
188
188
 
189
189
 
190
+ def set_network_tag(network_id, value):
191
+ _neutron = neutron()
192
+ _neutron.replace_tag('networks', network_id, {
193
+ 'tags': [value]
194
+ })
195
+
196
+
190
197
  ##########
191
198
  # Subnets
192
199
  ##########
@@ -638,6 +645,20 @@ def remove_subnet_from_router(router_id, subnet_id):
638
645
  # Floating IPs
639
646
  ###############
640
647
 
648
+ def set_floating_ip_tag(address, value):
649
+ ip_addr = get_floating_ip(address)
650
+ _neutron = neutron()
651
+ _neutron.replace_tag('floatingips', ip_addr['id'], {
652
+ 'tags': [value]
653
+ })
654
+
655
+
656
+ def deallocate_floating_ip(address):
657
+ _neutron = neutron()
658
+ _neutron.delete_floatingip(
659
+ get_floating_ip(address)["id"]
660
+ )
661
+
641
662
 
642
663
  def get_free_floating_ip(allocate=True) -> dict:
643
664
  """Get the first unallocated floating IP available to your project.
@@ -155,6 +155,7 @@ class Server:
155
155
  wait_for_active: bool = True,
156
156
  show: str = "widget",
157
157
  idempotent: bool = False,
158
+ **kwargs,
158
159
  ) -> "Server":
159
160
  """
160
161
  Submits a server creation request to the Nova API.
@@ -187,6 +188,7 @@ class Server:
187
188
  flavor=self.flavor_name,
188
189
  key=self.keypair.name,
189
190
  net_ids=[get_network_id(DEFAULT_NETWORK)],
191
+ **kwargs,
190
192
  )
191
193
  try:
192
194
  nova_server = self.conn.compute.create_server(**server_args)
@@ -274,6 +276,8 @@ class Server:
274
276
  return server
275
277
 
276
278
  def delete(self) -> None:
279
+ """Deletes the server.
280
+ """
277
281
  delete_server(self.id)
278
282
 
279
283
  def refresh(self):
@@ -422,7 +426,7 @@ class Server:
422
426
 
423
427
  def associate_floating_ip(self, fip: Optional[str] = None) -> None:
424
428
  """
425
- Associates a floating IP with the server. (BROKEN)
429
+ Associates a floating IP with the server.
426
430
 
427
431
  Args:
428
432
  fip (str, optional): The floating IP to associate with the server. If not provided, a new floating IP will be allocated.
@@ -435,7 +439,7 @@ class Server:
435
439
 
436
440
  def detach_floating_ip(self, fip: str) -> None:
437
441
  """
438
- Detaches a floating IP from the server. (BROKEN)
442
+ Detaches a floating IP from the server.
439
443
 
440
444
  Args:
441
445
  fip (str): The floating IP to detach.
@@ -562,6 +566,11 @@ class Server:
562
566
  with self.ssh_connection(**kwargs) as conn:
563
567
  return conn.run(command)
564
568
 
569
+ def get_metadata(self):
570
+ return nova().servers.list_meta(self.id)["metadata"]
571
+
572
+ def set_metadata_item(self, key, value):
573
+ return nova().servers.set_meta_item(self.id, key, value)
565
574
 
566
575
  ##########
567
576
  # Flavors
@@ -36,6 +36,17 @@ def utcnow():
36
36
  return datetime.now(tz=tz.tzutc())
37
37
 
38
38
 
39
+ def date_string_in_future(days=1):
40
+ return (utcnow() + timedelta(days=days)).isoformat()
41
+
42
+
43
+ def should_delete(delete_at):
44
+ try:
45
+ return datetime.fromisoformat(delete_at) < utcnow()
46
+ except ValueError:
47
+ return False
48
+
49
+
39
50
  class TimerProgressBar:
40
51
  def __init__(self):
41
52
  self.progress = widgets.IntProgress(
@@ -0,0 +1,92 @@
1
+ ===================
2
+ Python-chi Examples
3
+ ===================
4
+
5
+ .. _examples:
6
+
7
+ Cleanup Non-leased Resources
8
+ ============================
9
+
10
+
11
+ Opt-in Cleanup using tags
12
+ -------------------------
13
+
14
+ While baremetal nodes on Chameleon require leases, other resources like networks, and KVM instances
15
+ are persistent, and require manual cleanup. For large projects (e.g. for educational courses), it
16
+ may be useful to have some automatic process to clean up resources after a certain time. Below is
17
+ a suggested method for managing this. First, when creating the resource, or to prolong the resource,
18
+ you can set a "delete_at" tag. This is simply an date string in the future, which can be checked.
19
+
20
+ .. code-block:: python
21
+
22
+ # Set a Server metadata item, which will be read leader
23
+ my_server.set_metadata_item("delete_at", util.date_string_in_future(days=3))
24
+
25
+ # Similarly, use "tag" for network or floating ip
26
+ network.set_floating_ip_tag(MY_FIP_ADDRESS, f"delete_at={util.date_string_in_future(days=3)}")
27
+ network.set_network_tag(MY_NETWORK_ID, f"delete_at={util.date_string_in_future(days=3)}")
28
+
29
+ Then run the following script. You'll need to set authentication variables (e.g. source an openrc file),
30
+ and set the site/project at the beginning. This script iterates over the resources in your project, and
31
+ deletes them if the "delete_at" tag is in the past, if the user manually enters "yes" to confirm.
32
+ You can remove this confirmation in order to automatically run periodically with e.g. cron.
33
+
34
+ .. code-block:: python
35
+
36
+ from chi import util, server, network
37
+
38
+ # Find servers with "delete_at" metadata
39
+ for s in server.list_servers():
40
+ if util.should_delete(my_servers.get_metadata().get("delete_at", "")):
41
+ confirm = input(f"Delete server {s.name}? (yes/no): ").strip().lower()
42
+ if confirm == "yes":
43
+ s.delete()
44
+
45
+ # Networks, FIP tags set via tags as a string
46
+ for net in network.list_networks():
47
+ try:
48
+ delete_tag = next(s for s in net["tags"] if s.startswith("delete_at="))
49
+ if util.should_delete(delete_tag.split("=")[1]):
50
+ confirm = input(f"Delete the network {net['name']}? (yes/no): ").strip().lower()
51
+ if confirm == "yes":
52
+ # nuke_network deletes subnets, routers, and the network itself
53
+ network.nuke_network(net_id)
54
+ except StopIteration:
55
+ pass
56
+
57
+ for fip in network.list_floating_ips():
58
+ try:
59
+ delete_tag = next(s for s in fip["tags"] if s.startswith("delete_at="))
60
+ if util.should_delete(delete_tag.split("=")[1]):
61
+ confirm = input(f"Delete the floating ip {fip['floating_ip_address']}? (yes/no): ").strip().lower()
62
+ if confirm == "yes":
63
+ network.deallocate_floating_ip(fip["floating_ip_address"])
64
+ except StopIteration:
65
+ pass
66
+
67
+ Opt-in Cleanup using `created_at`
68
+ ---------------------------------
69
+
70
+ The above examples are explicitly opt-in, requring the user to set the "delete_at" tag to ensure that
71
+ nothing is deleted unknowlingly. Alternatively, you can monitor the "created_at" field of resources to
72
+ check for long-running resources such as in the following example.
73
+
74
+ .. code-block:: python
75
+
76
+ from datetime import datetime, timedelta
77
+ from chi import util, server, network
78
+
79
+ MAX_AGE = 3 # days
80
+ for s in server.list_servers():
81
+ age = util.utcnow() - datetime.fromisoformat(s.created_at)
82
+ if age > timedelta(days=MAX_AGE):
83
+ confirm = input(f"Delete server {s.name}? Age is {age}. (yes/no): ").strip().lower()
84
+ if confirm == "yes":
85
+ s.delete()
86
+ for net in network.list_networks():
87
+ age = util.utcnow() - datetime.fromisoformat(net["created_at"])
88
+ if age > timedelta(days=MAX_AGE):
89
+ confirm = input(f"Delete the network {net['name']}? Age is {age}. (yes/no): ").strip().lower()
90
+ if confirm == "yes":
91
+ # nuke_network deletes subnets, routers, and the network itself
92
+ network.nuke_network(net_id)
@@ -61,6 +61,9 @@ For more details about the modules available refer to their respective pages.
61
61
  chi.lease.create_lease(
62
62
  lease_name, reservations, start_date=start_date, end_date=end_date)
63
63
 
64
+ For further examples, see the `examples <examples>`_ section.
65
+
66
+
64
67
  Versions
65
68
  ========
66
69
  In python-chi version 1.0, we've deprecated older versions of functions that return
@@ -103,7 +106,8 @@ version of python-chi, this will no longer be needed.
103
106
  .. toctree::
104
107
  :caption: Examples
105
108
  :glob:
106
-
109
+
110
+ examples
107
111
  notebooks/*
108
112
 
109
113
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-chi
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Summary: Helper library for Chameleon Infrastructure (CHI) testbed
5
5
  Home-page: https://www.chameleoncloud.org
6
6
  Author: University of Chicago
@@ -33,6 +33,7 @@ chi/storage.py
33
33
  chi/util.py
34
34
  docs/__init__.py
35
35
  docs/conf.py
36
+ docs/examples.rst
36
37
  docs/generate_notebook.py
37
38
  docs/index.rst
38
39
  docs/requirements.txt
@@ -0,0 +1 @@
1
+ {"git_version": "fa6238a", "is_release": false}
@@ -5,5 +5,5 @@ from setuptools import setup
5
5
  setup(
6
6
  setup_requires=["pbr"],
7
7
  pbr=True,
8
- version='v1.0.2'
8
+ version='v1.0.4'
9
9
  )
@@ -1,7 +0,0 @@
1
- CHANGES
2
- =======
3
-
4
- v1.0.2
5
- ------
6
-
7
- * Add check for amount when using node\_type
@@ -1 +0,0 @@
1
- {"git_version": "b0c1918", "is_release": false}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes