Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@

This page was created to track changes to versions of Python-ESXi-Utilities (esxi_utils). The changelog was created in v3.22.1 and only changes starting from that version are tracked here.

## 4.0.0

- Changes the default method of retrieving VirtualMachine objects from their list
- From: Being scoped just to the 'child' server in a vCenter
- To: All available virtual machines as visible via the vCenter inventory
- The old method of getting a list of virtual machines is available by specifying legacy_list=True to the ESXiClient object when creating it

- Adds support for Virtual Machine 'Templates' in vCenter arrangements
- vm.is_template() for determining if a VM has been converted to a template
- vm.to_template() to convert a VM to a clonable template (THIS CANNOT BE UNDONE)
- vm.deploy_from_template(...) to create a new VM from a VM template
- Adds a new client.is_vcenter() method to the ESXi client object
- Adds new properties to VirtualMachine object for determining their runtime (child) ESXi host:
- vm.host_system reveals the vim.HostSystem
- vm.esxi_host_name reveals the 'hostname' of the ESXi (child) server owning this VM's resources
- Adds new parameters (host and resource_pool) to vm.create and vm.upload to control which ESXi (child) host and resource pool is deployed to (default is still the legacy operation of 'the currently connected host')
- VirtualMachine to 'str' now attempts to show the runtime host instead of the connected child server in vCenter
- Fix several regex warnings from this library in Python version 3.12+ by adding the 'raw string' character in front of the regex strings
- Silences the deprecation warning coming from this library about the pinning of setuptools pkg_resources API

## 3.22.1

- Adds metadata to pip package for PyPI
Expand Down
23 changes: 16 additions & 7 deletions esxi_utils/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ class ESXiClient:
:param child_hostname: When connecting to a vCenter instance, the hostname or IP of the child ESXi server.
:param child_username: When connecting to a vCenter instance, the username to login to the child ESXi server (for SSH).
:param child_password: When connecting to a vCenter instance, the password of the child ESXi user.
:param use_legacy_vm_list: When collecting all Virtual Machines from the ESXi server, this setting decides whether the VMs collected are only from the child server on vCenter (True) or if the VM list contains all VMs as seen via the vCenter inventory (False).
"""
def __init__(self, hostname: str, username: str, password: str, child_hostname: typing.Optional[str] = None, child_username: typing.Optional[str] = None, child_password: typing.Optional[str] = None):
def __init__(self, hostname: str, username: str, password: str, child_hostname: typing.Optional[str] = None, child_username: typing.Optional[str] = None, child_password: typing.Optional[str] = None, use_legacy_vm_list: bool = False):
self.use_legacy_vm_list = use_legacy_vm_list
context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
context.verify_mode = ssl.CERT_NONE
err = None
Expand Down Expand Up @@ -117,7 +119,7 @@ def vms(self) -> 'VirtualMachineList':
"""
The Virtual Machine handler for this host.
"""
return VirtualMachineList(self)
return VirtualMachineList(self, container=None, legacy_list=self.use_legacy_vm_list)

@property
def firewall(self) -> 'Firewall':
Expand Down Expand Up @@ -280,6 +282,12 @@ def __repr__(self):
def __del__(self):
self.close()

def _content(self):
return self._service_instance.RetrieveContent()

def is_vcenter(self) -> 'bool':
return str(self._content().about.apiType).lower() == "virtualcenter"

def _get_vim_objects_from(self, root, vim_type):
"""
Search the ESXi server for the all instances of the specified vim object under the given root object.
Expand All @@ -291,10 +299,11 @@ def _get_vim_objects_from(self, root, vim_type):
"""
if not isinstance(vim_type, list):
vim_type = [vim_type]
content = self._service_instance.RetrieveContent()
container = content.viewManager.CreateContainerView(root, vim_type, True)
objs = [ ref for ref in container.view ]
container.Destroy()
try:
container = self._content().viewManager.CreateContainerView(root, vim_type, True)
objs = [ ref for ref in container.view ]
finally:
container.Destroy()
return objs

def _get_vim_objects(self, vim_type, query_root: bool = False):
Expand All @@ -309,7 +318,7 @@ def _get_vim_objects(self, vim_type, query_root: bool = False):
# request only content based on the currently connected 'host_system' server
if not query_root and self._child_hostname and self._host_system:
return self._get_vim_objects_from(self._host_system, vim_type)
return self._get_vim_objects_from(self._service_instance.RetrieveContent().rootFolder, vim_type)
return self._get_vim_objects_from(self._content().rootFolder, vim_type)

def _get_vim_object(self, vim_type, name: str):
"""
Expand Down
2 changes: 1 addition & 1 deletion esxi_utils/datastore.py
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,7 @@ def parse(client: 'ESXiClient', filepath: str) -> 'DatastoreFile':

if filepath.startswith("["):
try:
match = re.match("\[(" + "|".join([ re.escape(name) for name in client.datastores.names ]) + ")\]\s*(.+)", filepath, flags=re.IGNORECASE)
match = re.match(r"\[(" + "|".join([ re.escape(name) for name in client.datastores.names ]) + r")\]\s*(.+)", filepath, flags=re.IGNORECASE)
datastore_name = match.group(1)
relative_path = match.group(2).lstrip("/")
return DatastoreFile(client.datastores.get(datastore_name), relative_path)
Expand Down
2 changes: 1 addition & 1 deletion esxi_utils/firewall/firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def _update_service_xml(self, new_tree):
:param new_tree: The `etree` XML object to write to the service file.
"""
new_content = etree.tostring(new_tree, pretty_print=True, encoding='unicode')
new_content = re.sub(r"</service>", "\g<0>\n", new_content, flags=re.MULTILINE)
new_content = re.sub(r"</service>", r"\g<0>\n", new_content, flags=re.MULTILINE)
with self._client.ssh() as conn:
err = None
try:
Expand Down
11 changes: 10 additions & 1 deletion esxi_utils/util/connect/cisco.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
from esxi_utils.util.connect.ssh import SSHConnection
from dateutil.parser import parse as parsetime

# Silence the deprecation warning for pkg_resources for now
import warnings
warnings.filterwarnings(
"ignore",
category=UserWarning,
message=r".*pkg_resources is deprecated as an API.*",
)

import pkg_resources
import datetime
import textfsm
Expand Down Expand Up @@ -173,7 +182,7 @@ def _parse_table(self, stringtable, headers):
"""
# Remove empty lines and whitespace lines
lines = list(filter(lambda str: str and not str.isspace(),stringtable.splitlines()))
regex = re.compile('\s*'+'\s*'.join([re.escape(head) for head in headers])+'\s*')
regex = re.compile(r'\s*'+r'\s*'.join([re.escape(head) for head in headers])+r'\s*')

# Find the header of the table
while lines and not regex.match(lines[0]):
Expand Down
2 changes: 1 addition & 1 deletion esxi_utils/util/connect/panos.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ def show_routing_ospf(self) -> typing.Dict[str, typing.Any]:
routes.append(value)

flags = {}
for m in re.findall('[^\s,]+:[^\s,]+', xml.findtext("./result/flags")):
for m in re.findall(r'[^\s,]+:[^\s,]+', xml.findtext("./result/flags")):
field, value = m.split(':')
flags[field] = value

Expand Down
Loading
Loading