[virt-who] Fix adding https:// to ESX url
by Radek Novacek
commit dbef0c2b5f54118677b73a08edb963e185631ea1
Author: Radek Novacek <rnovacek(a)redhat.com>
Date: Thu Oct 25 11:14:14 2012 +0200
Fix adding https:// to ESX url
vsphere.py | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
---
diff --git a/vsphere.py b/vsphere.py
index 3dffca9..46b5fb3 100644
--- a/vsphere.py
+++ b/vsphere.py
@@ -114,9 +114,9 @@ class VSphere:
self.url = "https://%s" % self.url
# Connect to the vCenter server
- self.client = suds.client.Client("%s/sdk/vimService.wsdl" % url)
+ self.client = suds.client.Client("%s/sdk/vimService.wsdl" % self.url)
- self.client.set_options(location="%s/sdk" % url)
+ self.client.set_options(location="%s/sdk" % self.url)
# Get Meta Object Reference to ServiceInstance which is the root object of the inventory
self.moRef = suds.sudsobject.Property('ServiceInstance')
11 years, 6 months
[virt-who] Help and manpage improvements
by Radek Novacek
commit 690929f3e2d5169ad143d225cda24880722eae82
Author: Radek Novacek <rnovacek(a)redhat.com>
Date: Wed Oct 24 10:13:41 2012 +0200
Help and manpage improvements
virt-who.8 | 2 +-
virt-who.py | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
---
diff --git a/virt-who.8 b/virt-who.8
index 4b2c283..715cce1 100644
--- a/virt-who.8
+++ b/virt-who.8
@@ -150,7 +150,7 @@ Use ESX (vCenter) as virtualization backend and specify option required to conne
Use RHEV-M as virtualization backend and specify option required to connect to RHEV-M server.
.TP
-4. Hyper-V
+5. Hyper-V
# virt-who --hyperv --hyperv-owner=HYPERV_OWNER --hyperv-env=HYPERV_ENV --hyperv-server=HYPERV_SERVER --hyperv-username=HYPERV_USERNAME --hyperv-password=HYPERV_PASSWORD
diff --git a/virt-who.py b/virt-who.py
index 85d998a..52ec318 100644
--- a/virt-who.py
+++ b/virt-who.py
@@ -341,8 +341,8 @@ def main():
rhevmGroup = OptionGroup(parser, "RHEV-M options", "Use this options with --rhevm")
rhevmGroup.add_option("--rhevm-owner", action="store", dest="owner", default="", help="Organization who has purchased subscriptions of the products")
rhevmGroup.add_option("--rhevm-env", action="store", dest="env", default="", help="Environment where the RHEV-M belongs to")
- rhevmGroup.add_option("--rhevm-server", action="store", dest="server", default="", help="URL of the RHEV-M server to connect to")
- rhevmGroup.add_option("--rhevm-username", action="store", dest="username", default="", help="Username for connecting to RHEV-M")
+ rhevmGroup.add_option("--rhevm-server", action="store", dest="server", default="", help="URL of the RHEV-M server to connect to (preferable use secure connection - https://<ip or domain name>:<secure port, usually 8443>)")
+ rhevmGroup.add_option("--rhevm-username", action="store", dest="username", default="", help="Username for connecting to RHEV-M in the format username@domain")
rhevmGroup.add_option("--rhevm-password", action="store", dest="password", default="", help="Password for connecting to RHEV-M")
parser.add_option_group(rhevmGroup)
11 years, 6 months
[virt-who] Fix couple of issues in Hyper-V support
by Radek Novacek
commit 38b1ef40e06454b490ad004f0df1020b8a1f2fe2
Author: Radek Novacek <rnovacek(a)redhat.com>
Date: Wed Oct 17 10:59:56 2012 +0200
Fix couple of issues in Hyper-V support
hyperv.py | 6 +++++-
virt-who.py | 4 ++--
2 files changed, 7 insertions(+), 3 deletions(-)
---
diff --git a/hyperv.py b/hyperv.py
index 1727204..2fcb1a3 100644
--- a/hyperv.py
+++ b/hyperv.py
@@ -266,7 +266,8 @@ class HyperV:
guests = []
connection, headers = self.connect()
hypervsoap = HyperVSoap(self.url, connection, headers)
- uuid = hypervsoap.Enumerate("select BIOSGUID from Msvm_VirtualSystemSettingData")
+ # SettingType == 3 means current setting, 5 is snapshot - we don't want snapshots
+ uuid = hypervsoap.Enumerate("select BIOSGUID from Msvm_VirtualSystemSettingData where SettingType = 3")
for instance in hypervsoap.Pull(uuid):
guests.append(HyperV.decodeWinUUID(instance["BIOSGUID"]))
uuid = hypervsoap.Enumerate("select UUID from Win32_ComputerSystemProduct", "root/cimv2")
@@ -275,6 +276,9 @@ class HyperV:
host = HyperV.decodeWinUUID(instance["UUID"])
return { host: guests }
+ def ping(self):
+ return True
+
if __name__ == '__main__':
# TODO: read from config
if len(sys.argv) < 4:
diff --git a/virt-who.py b/virt-who.py
index d121e80..85d998a 100644
--- a/virt-who.py
+++ b/virt-who.py
@@ -346,7 +346,7 @@ def main():
rhevmGroup.add_option("--rhevm-password", action="store", dest="password", default="", help="Password for connecting to RHEV-M")
parser.add_option_group(rhevmGroup)
- hypervGroup = OptionGroup(parser, "RHEV-M options", "Use this options with --hyperv")
+ hypervGroup = OptionGroup(parser, "Hyper-V options", "Use this options with --hyperv")
hypervGroup.add_option("--hyperv-owner", action="store", dest="owner", default="", help="Organization who has purchased subscriptions of the products")
hypervGroup.add_option("--hyperv-env", action="store", dest="env", default="", help="Environment where the Hyper-V belongs to")
hypervGroup.add_option("--hyperv-server", action="store", dest="server", default="", help="URL of the Hyper-V server to connect to")
@@ -459,7 +459,7 @@ def main():
logger.debug("Starting event loop")
virEventLoopPureStart()
else:
- logger.warning("Listening for events is not available in VDSM, ESX or RHEV-M mode")
+ logger.warning("Listening for events is not available in VDSM, ESX, RHEV-M or Hyper-V mode")
global RetryInterval
if options.interval < RetryInterval:
11 years, 6 months
[virt-who] Add support for Hyper-V hypervisor.
by Radek Novacek
commit 2343e043370d7a73b60a488e84d2917bc6cef3e3
Author: Radek Novacek <rnovacek(a)redhat.com>
Date: Thu Oct 11 14:07:12 2012 +0200
Add support for Hyper-V hypervisor.
README.hyperv | 28 ++++
hyperv.py | 288 +++++++++++++++++++++++++++++++++
ntlm.py | 494 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
virt-who.8 | 31 ++++-
virt-who.conf | 11 ++-
virt-who.py | 29 +++-
6 files changed, 877 insertions(+), 4 deletions(-)
---
diff --git a/README.hyperv b/README.hyperv
new file mode 100644
index 0000000..532f64e
--- /dev/null
+++ b/README.hyperv
@@ -0,0 +1,28 @@
+In order to make virt-who connection to Hyper-V work, some steps needs to be done first.
+
+1. Windows Remote Management must be enabled and HTTP or HTTPS listener must be running.
+
+ Following command can be used on Hyper-V server:
+
+ winrm quickconfig
+
+
+2. Firewall must allow Remote Administration
+
+ Following command can be used on Hyper-V server:
+
+ netsh advfirewall firewall set rule group="Remote Administration" new enable=yes
+
+
+3. Unencrypted connection must be enabled for HTTP (not required for HTTPS)
+
+ Following command can be used on Hyper-V server:
+
+ winrm set winrm/config/service @{AllowUnencrypted="true"}
+
+
+4. Only Basic and NTLM authentication methods are supported
+
+ Verify that at least one of methods Basic or Negotiate is enabled (True)
+
+ winrm get winrm/config/service/auth
diff --git a/hyperv.py b/hyperv.py
new file mode 100644
index 0000000..1727204
--- /dev/null
+++ b/hyperv.py
@@ -0,0 +1,288 @@
+
+import sys
+import httplib
+import urlparse
+import base64
+from uuid import uuid1
+
+# Import XML parser
+try:
+ from elementtree import ElementTree
+except ImportError:
+ from xml.etree import ElementTree
+
+import ntlm
+
+NAMESPACES = {
+ 's': 'http://www.w3.org/2003/05/soap-envelope',
+ 'wsa': 'http://schemas.xmlsoap.org/ws/2004/08/addressing',
+ 'wsman': 'http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd',
+ 'wsen': 'http://schemas.xmlsoap.org/ws/2004/09/enumeration'
+}
+
+ENVELOPE = """<?xml version="1.0" encoding="UTF-8"?>
+<s:Envelope """ + " ".join(('xmlns:%s="%s"' % (k, v) for k, v in NAMESPACES.items())) + """>
+ %s
+ %s
+</s:Envelope>"""
+
+def getHeader(action):
+ return """<s:Header>
+ <wsa:Action s:mustUnderstand="true">""" + NAMESPACES['wsen'] + "/" + action + """</wsa:Action>
+ <wsa:To s:mustUnderstand="true">%(url)s</wsa:To>
+ <wsman:ResourceURI s:mustUnderstand="true">http://schemas.microsoft.com/wbem/wsman/1/wmi/%(namespace)s/*</wsman:ResourceURI>
+ <wsa:MessageID s:mustUnderstand="true">uuid:""" + str(uuid1()) + """</wsa:MessageID>
+ <wsa:ReplyTo>
+ <wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
+ </wsa:ReplyTo>
+ </s:Header>"""
+
+ENUMERATE_BODY = """<s:Body>
+ <wsen:Enumerate>
+ <wsman:Filter Dialect="http://schemas.microsoft.com/wbem/wsman/1/WQL">%(query)s</wsman:Filter>
+ </wsen:Enumerate>
+ </s:Body>"""
+
+PULL_BODY = """<s:Body>
+ <wsen:Pull>
+ <wsen:EnumerationContext>%(EnumerationContext)s</wsen:EnumerationContext>
+ </wsen:Pull>
+ </s:Body>"""
+
+ENUMERATE_XML = ENVELOPE % (getHeader("Enumerate"), ENUMERATE_BODY)
+PULL_XML = ENVELOPE % (getHeader("Pull"), PULL_BODY)
+
+class HyperVSoap(object):
+ def __init__(self, url, connection, headers):
+ self.url = url
+ self.connection = connection
+ self.headers = headers
+
+ def post(self, body):
+ self.headers["Content-Length"] = "%d" % len(body)
+ self.connection.request("POST", self.url, body=body, headers=self.headers)
+ response = self.connection.getresponse()
+ if response.status == 401:
+ raise HyperVAuthFailed("Authentication failed")
+ if response.status != 200:
+ raise HyperVException("Communication with Hyper-V failed, HTTP error: %d" % response.status)
+ if response is None:
+ raise HyperVException("No reply from Hyper-V")
+ return response
+
+ @classmethod
+ def _Instance(cls, xml):
+ def stripNamespace(tag):
+ return tag[tag.find("}") + 1:]
+ children = xml.getchildren()
+ if len(children) < 1:
+ return None
+ child = children[0]
+ properties = {}
+ for ch in child.getchildren():
+ properties[stripNamespace(ch.tag)] = ch.text
+ return properties
+
+ def Enumerate(self, query, namespace="root/virtualization"):
+ data = ENUMERATE_XML % { 'url': self.url, 'query': query, 'namespace': namespace }
+ response = self.post(data)
+ d = response.read()
+ xml = ElementTree.fromstring(d)
+ if xml.tag != "{%(s)s}Envelope" % NAMESPACES:
+ raise HyperVException("Wrong reply format")
+ responses = xml.findall("{%(s)s}Body/{%(wsen)s}EnumerateResponse" % NAMESPACES)
+ if len(responses) < 1:
+ raise HyperVException("Wrong reply format")
+ contexts = responses[0].getchildren()
+ if len(contexts) < 1:
+ raise HyperVException("Wrong reply format")
+
+ if contexts[0].tag != "{%(wsen)s}EnumerationContext" % NAMESPACES:
+ raise HyperVException("Wrong reply format")
+ return contexts[0].text
+
+ def _PullOne(self, uuid, namespace):
+ data = PULL_XML % { 'url': self.url, 'EnumerationContext': uuid, 'namespace': namespace }
+ response = self.post(data)
+ d = response.read()
+ xml = ElementTree.fromstring(d)
+ if xml.tag != "{%(s)s}Envelope" % NAMESPACES:
+ raise HyperVException("Wrong reply format")
+ responses = xml.findall("{%(s)s}Body/{%(wsen)s}PullResponse" % NAMESPACES)
+ if len(responses) < 0:
+ raise HyperVException("Wrong reply format")
+
+ uuid = None
+ instance = None
+
+ for node in responses[0].getchildren():
+ if node.tag == "{%(wsen)s}EnumerationContext" % NAMESPACES:
+ uuid = node.text
+ elif node.tag == "{%(wsen)s}Items" % NAMESPACES:
+ instance = HyperVSoap._Instance(node)
+
+ return uuid, instance
+
+ def Pull(self, uuid, namespace="root/virtualization"):
+ instances = []
+ while uuid is not None:
+ uuid, instance = self._PullOne(uuid, namespace)
+ if instance is not None:
+ instances.append(instance)
+ return instances
+
+
+class HyperVException(Exception):
+ pass
+
+class HyperVAuthFailed(HyperVException):
+ pass
+
+
+class HyperV:
+ def __init__(self, logger, url, username, password):
+ self.logger = logger
+ self.username = username
+ self.password = password
+
+ # Parse URL and create proper one
+ if "//" not in url:
+ url = "//" + url
+ parsed = urlparse.urlsplit(url, "http")
+ if ":" not in parsed[1]:
+ if parsed[0] == "https":
+ self.host = parsed[1] + ":5986"
+ else:
+ self.host = parsed[1] + ":5985"
+ else:
+ self.host = parsed[1]
+ if parsed[2] == "":
+ path = "wsman"
+ else:
+ path = parsed[2]
+ self.url = urlparse.urlunsplit((parsed[0], self.host, path, "", ""))
+
+ logger.debug("Hyper-V url: %s" % self.url)
+
+ # Check if we have domain defined and set flags accordingly
+ user_parts = username.split('\\', 1)
+ if len(user_parts) == 1:
+ self.username = user_parts[0]
+ self.domainname = ''
+ self.type1_flags = ntlm.NTLM_TYPE1_FLAGS & ~ntlm.NTLM_NegotiateOemDomainSupplied
+ else:
+ self.domainname = user_parts[0].upper()
+ self.username = user_parts[1]
+ self.type1_flags = ntlm.NTLM_TYPE1_FLAGS
+
+ def connect(self):
+ if self.url.startswith("https"):
+ connection = httplib.HTTPSConnection(self.host)
+ else:
+ connection = httplib.HTTPConnection(self.host)
+
+ headers = {}
+ headers["Connection"] = "Keep-Alive"
+ headers["Content-Length"] = "0"
+
+ connection.request("POST", self.url, headers=headers)
+ response = connection.getresponse()
+ response.read()
+ if response.status == 200:
+ return connection, headers
+ elif response.status == 404:
+ raise HyperVException("Invalid HyperV url: %s" % self.url)
+ elif response.status != 401:
+ raise HyperVException("Unable to connect to HyperV at: %s" % self.url)
+ # 401 - need authentication
+
+ authenticate_header = response.getheader("WWW-Authenticate", "")
+ if 'Negotiate' in authenticate_header:
+ try:
+ self.ntlmAuth(connection, headers)
+ except HyperVAuthFailed:
+ if 'Basic' in authenticate_header:
+ self.basicAuth(connection, headers)
+ else:
+ raise
+ elif 'Basic' in authenticate_header:
+ self.basicAuth(connection, headers)
+ else:
+ raise HyperVAuthFailed("Server doesn't known any supported authentication method")
+ return connection, headers
+
+ def ntlmAuth(self, connection, headers):
+ self.logger.debug("Using NTLM authentication")
+ # Use ntlm
+ headers["Authorization"] = "Negotiate %s" % ntlm.create_NTLM_NEGOTIATE_MESSAGE(self.username, self.type1_flags)
+
+ connection.request("POST", self.url, headers=headers)
+ response = connection.getresponse()
+ response.read()
+ if response.status != 401:
+ raise HyperVAuthFailed("NTLM negotiation failed")
+
+ auth_header = response.getheader("WWW-Authenticate", "")
+ if auth_header == "":
+ raise HyperVAuthFailed("NTLM negotiation failed")
+
+ nego, challenge = auth_header.split(" ")
+ if nego != "Negotiate":
+ print >>sys.stderr, "Wrong header: ", auth_header
+ sys.exit(1)
+
+ nonce, flags = ntlm.parse_NTLM_CHALLENGE_MESSAGE(challenge)
+ headers["Authorization"] = "Negotiate %s" % ntlm.create_NTLM_AUTHENTICATE_MESSAGE(nonce, self.username, self.domainname, self.password, flags)
+
+ connection.request("POST", self.url, headers=headers)
+ response = connection.getresponse()
+ response.read()
+ if response.status == 200:
+ headers.pop("Authorization")
+ self.logger.debug("NTLM authentication successful")
+ else:
+ raise HyperVAuthFailed("NTLM negotiation failed")
+
+ def basicAuth(self, connection, headers):
+ self.logger.debug("Using Basic authentication")
+
+ passphrase = "%s:%s" % (self.username, self.password)
+ encoded = base64.encodestring(passphrase)
+ headers["Authorization"] = "Basic %s" % encoded.replace('\n', '')
+
+ @classmethod
+ def decodeWinUUID(cls, uuid):
+ """ Windows UUID needs to be decoded using following key
+ From: {78563412-AB90-EFCD-1234-567890ABCDEF}
+ To: 12345678-90AB-CDEF-1234-567890ABCDEF
+ """
+ if uuid[0] == "{":
+ s = uuid[1:-1]
+ else:
+ s = uuid
+ return s[6:8] + s[4:6] + s[2:4] + s[0:2] + "-" + s[11:13] + s[9:11] + "-" + s[16:18] + s[14:16] + s[18:]
+
+ def getHostGuestMapping(self):
+ guests = []
+ connection, headers = self.connect()
+ hypervsoap = HyperVSoap(self.url, connection, headers)
+ uuid = hypervsoap.Enumerate("select BIOSGUID from Msvm_VirtualSystemSettingData")
+ for instance in hypervsoap.Pull(uuid):
+ guests.append(HyperV.decodeWinUUID(instance["BIOSGUID"]))
+ uuid = hypervsoap.Enumerate("select UUID from Win32_ComputerSystemProduct", "root/cimv2")
+ host = None
+ for instance in hypervsoap.Pull(uuid, "root/cimv2"):
+ host = HyperV.decodeWinUUID(instance["UUID"])
+ return { host: guests }
+
+if __name__ == '__main__':
+ # TODO: read from config
+ if len(sys.argv) < 4:
+ print "Usage: %s url username password"
+ sys.exit(0)
+
+ import logging
+ logger = logging.Logger("")
+ logger.addHandler(logging.StreamHandler())
+ hyperv = HyperV(logger, sys.argv[1], sys.argv[2], sys.argv[3])
+ print hyperv.getHostGuestMapping()
diff --git a/ntlm.py b/ntlm.py
new file mode 100644
index 0000000..d950611
--- /dev/null
+++ b/ntlm.py
@@ -0,0 +1,494 @@
+# This library is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation, either
+# version 3 of the License, or (at your option) any later version.
+
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library. If not, see <http://www.gnu.org/licenses/> or <http://www.gnu.org/licenses/lgpl.txt>.
+
+import struct
+import base64
+import string
+import hashlib
+import hmac
+import random
+from socket import gethostname
+
+import M2Crypto
+
+
+# DES handling functions
+
+def des_encrypt(key_str, plain_text):
+ k = str_to_key56(key_str)
+ k = key56_to_key64(k)
+ key_str = ''
+ for i in k:
+ key_str += chr(i & 0xFF)
+ des = M2Crypto.EVP.Cipher("des_ecb", key=key_str, op=M2Crypto.encrypt, iv='\0'*16)
+
+ return des.update(plain_text)
+
+def str_to_key56(key_str):
+ if len(key_str) < 7:
+ key_str = key_str + '\000\000\000\000\000\000\000'[:(7 - len(key_str))]
+ key_56 = []
+ for i in key_str[:7]: key_56.append(ord(i))
+
+ return key_56
+
+def key56_to_key64(key_56):
+ ""
+ key = []
+ for i in range(8): key.append(0)
+
+ key[0] = key_56[0];
+ key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);
+ key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);
+ key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);
+ key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);
+ key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);
+ key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);
+ key[7] = (key_56[6] << 1) & 0xFF;
+
+ key = set_key_odd_parity(key)
+
+ return key
+
+def set_key_odd_parity(key):
+ ""
+ for i in range(len(key)):
+ for k in range(7):
+ bit = 0
+ t = key[i] >> k
+ bit = (t ^ bit) & 0x1
+ key[i] = (key[i] & 0xFE) | bit
+
+ return key
+
+# NTLM implemntation
+
+NTLM_NegotiateUnicode = 0x00000001
+NTLM_NegotiateOEM = 0x00000002
+NTLM_RequestTarget = 0x00000004
+NTLM_Unknown9 = 0x00000008
+NTLM_NegotiateSign = 0x00000010
+NTLM_NegotiateSeal = 0x00000020
+NTLM_NegotiateDatagram = 0x00000040
+NTLM_NegotiateLanManagerKey = 0x00000080
+NTLM_Unknown8 = 0x00000100
+NTLM_NegotiateNTLM = 0x00000200
+NTLM_NegotiateNTOnly = 0x00000400
+NTLM_Anonymous = 0x00000800
+NTLM_NegotiateOemDomainSupplied = 0x00001000
+NTLM_NegotiateOemWorkstationSupplied = 0x00002000
+NTLM_Unknown6 = 0x00004000
+NTLM_NegotiateAlwaysSign = 0x00008000
+NTLM_TargetTypeDomain = 0x00010000
+NTLM_TargetTypeServer = 0x00020000
+NTLM_TargetTypeShare = 0x00040000
+NTLM_NegotiateExtendedSecurity = 0x00080000
+NTLM_NegotiateIdentify = 0x00100000
+NTLM_Unknown5 = 0x00200000
+NTLM_RequestNonNTSessionKey = 0x00400000
+NTLM_NegotiateTargetInfo = 0x00800000
+NTLM_Unknown4 = 0x01000000
+NTLM_NegotiateVersion = 0x02000000
+NTLM_Unknown3 = 0x04000000
+NTLM_Unknown2 = 0x08000000
+NTLM_Unknown1 = 0x10000000
+NTLM_Negotiate128 = 0x20000000
+NTLM_NegotiateKeyExchange = 0x40000000
+NTLM_Negotiate56 = 0x80000000
+
+# we send these flags with our type 1 message
+NTLM_TYPE1_FLAGS = (NTLM_NegotiateUnicode | \
+ NTLM_NegotiateOEM | \
+ NTLM_RequestTarget | \
+ NTLM_NegotiateNTLM | \
+ NTLM_NegotiateOemDomainSupplied | \
+ NTLM_NegotiateOemWorkstationSupplied | \
+ NTLM_NegotiateAlwaysSign | \
+ NTLM_NegotiateExtendedSecurity | \
+ NTLM_NegotiateVersion | \
+ NTLM_Negotiate128 | \
+ NTLM_Negotiate56 )
+NTLM_TYPE2_FLAGS = (NTLM_NegotiateUnicode | \
+ NTLM_RequestTarget | \
+ NTLM_NegotiateNTLM | \
+ NTLM_NegotiateAlwaysSign | \
+ NTLM_NegotiateExtendedSecurity | \
+ NTLM_NegotiateTargetInfo | \
+ NTLM_NegotiateVersion | \
+ NTLM_Negotiate128 | \
+ NTLM_Negotiate56)
+
+NTLM_MsvAvEOL = 0 # Indicates that this is the last AV_PAIR in the list. AvLen MUST be 0. This type of information MUST be present in the AV pair list.
+NTLM_MsvAvNbComputerName = 1 # The server's NetBIOS computer name. The name MUST be in Unicode, and is not null-terminated. This type of information MUST be present in the AV_pair list.
+NTLM_MsvAvNbDomainName = 2 # The server's NetBIOS domain name. The name MUST be in Unicode, and is not null-terminated. This type of information MUST be present in the AV_pair list.
+NTLM_MsvAvDnsComputerName = 3 # The server's Active Directory DNS computer name. The name MUST be in Unicode, and is not null-terminated.
+NTLM_MsvAvDnsDomainName = 4 # The server's Active Directory DNS domain name. The name MUST be in Unicode, and is not null-terminated.
+NTLM_MsvAvDnsTreeName = 5 # The server's Active Directory (AD) DNS forest tree name. The name MUST be in Unicode, and is not null-terminated.
+NTLM_MsvAvFlags = 6 # A field containing a 32-bit value indicating server or client configuration. 0x00000001: indicates to the client that the account authentication is constrained. 0x00000002: indicates that the client is providing message integrity in the MIC field (section 2.2.1.3) in the AUTHENTICATE_MESSAGE.
+NTLM_MsvAvTimestamp = 7 # A FILETIME structure ([MS-DTYP] section 2.3.1) in little-endian byte order that contains the server local time.<12>
+NTLM_MsAvRestrictions = 8 #A Restriction_Encoding structure (section 2.2.2.2). The Value field contains a structure representing the integrity level of the security principal, as well as a MachineID created at computer startup to identify the calling machine. <13>
+
+
+"""
+utility functions for Microsoft NTLM authentication
+
+References:
+[MS-NLMP]: NT LAN Manager (NTLM) Authentication Protocol Specification
+http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-NLMP%5D.pdf
+
+[MS-NTHT]: NTLM Over HTTP Protocol Specification
+http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-NTHT%5D.pdf
+
+Cntlm Authentication Proxy
+http://cntlm.awk.cz/
+
+NTLM Authorization Proxy Server
+http://sourceforge.net/projects/ntlmaps/
+
+Optimized Attack for NTLM2 Session Response
+http://www.blackhat.com/presentations/bh-asia-04/bh-jp-04-pdfs/bh-jp-04-seki.pdf
+"""
+def dump_NegotiateFlags(NegotiateFlags):
+ if NegotiateFlags & NTLM_NegotiateUnicode:
+ print "NTLM_NegotiateUnicode set"
+ if NegotiateFlags & NTLM_NegotiateOEM:
+ print "NTLM_NegotiateOEM set"
+ if NegotiateFlags & NTLM_RequestTarget:
+ print "NTLM_RequestTarget set"
+ if NegotiateFlags & NTLM_Unknown9:
+ print "NTLM_Unknown9 set"
+ if NegotiateFlags & NTLM_NegotiateSign:
+ print "NTLM_NegotiateSign set"
+ if NegotiateFlags & NTLM_NegotiateSeal:
+ print "NTLM_NegotiateSeal set"
+ if NegotiateFlags & NTLM_NegotiateDatagram:
+ print "NTLM_NegotiateDatagram set"
+ if NegotiateFlags & NTLM_NegotiateLanManagerKey:
+ print "NTLM_NegotiateLanManagerKey set"
+ if NegotiateFlags & NTLM_Unknown8:
+ print "NTLM_Unknown8 set"
+ if NegotiateFlags & NTLM_NegotiateNTLM:
+ print "NTLM_NegotiateNTLM set"
+ if NegotiateFlags & NTLM_NegotiateNTOnly:
+ print "NTLM_NegotiateNTOnly set"
+ if NegotiateFlags & NTLM_Anonymous:
+ print "NTLM_Anonymous set"
+ if NegotiateFlags & NTLM_NegotiateOemDomainSupplied:
+ print "NTLM_NegotiateOemDomainSupplied set"
+ if NegotiateFlags & NTLM_NegotiateOemWorkstationSupplied:
+ print "NTLM_NegotiateOemWorkstationSupplied set"
+ if NegotiateFlags & NTLM_Unknown6:
+ print "NTLM_Unknown6 set"
+ if NegotiateFlags & NTLM_NegotiateAlwaysSign:
+ print "NTLM_NegotiateAlwaysSign set"
+ if NegotiateFlags & NTLM_TargetTypeDomain:
+ print "NTLM_TargetTypeDomain set"
+ if NegotiateFlags & NTLM_TargetTypeServer:
+ print "NTLM_TargetTypeServer set"
+ if NegotiateFlags & NTLM_TargetTypeShare:
+ print "NTLM_TargetTypeShare set"
+ if NegotiateFlags & NTLM_NegotiateExtendedSecurity:
+ print "NTLM_NegotiateExtendedSecurity set"
+ if NegotiateFlags & NTLM_NegotiateIdentify:
+ print "NTLM_NegotiateIdentify set"
+ if NegotiateFlags & NTLM_Unknown5:
+ print "NTLM_Unknown5 set"
+ if NegotiateFlags & NTLM_RequestNonNTSessionKey:
+ print "NTLM_RequestNonNTSessionKey set"
+ if NegotiateFlags & NTLM_NegotiateTargetInfo:
+ print "NTLM_NegotiateTargetInfo set"
+ if NegotiateFlags & NTLM_Unknown4:
+ print "NTLM_Unknown4 set"
+ if NegotiateFlags & NTLM_NegotiateVersion:
+ print "NTLM_NegotiateVersion set"
+ if NegotiateFlags & NTLM_Unknown3:
+ print "NTLM_Unknown3 set"
+ if NegotiateFlags & NTLM_Unknown2:
+ print "NTLM_Unknown2 set"
+ if NegotiateFlags & NTLM_Unknown1:
+ print "NTLM_Unknown1 set"
+ if NegotiateFlags & NTLM_Negotiate128:
+ print "NTLM_Negotiate128 set"
+ if NegotiateFlags & NTLM_NegotiateKeyExchange:
+ print "NTLM_NegotiateKeyExchange set"
+ if NegotiateFlags & NTLM_Negotiate56:
+ print "NTLM_Negotiate56 set"
+
+def create_NTLM_NEGOTIATE_MESSAGE(user, type1_flags=NTLM_TYPE1_FLAGS):
+ BODY_LENGTH = 40
+ Payload_start = BODY_LENGTH # in bytes
+ protocol = 'NTLMSSP\0' #name
+
+ type = struct.pack('<I',1) #type 1
+
+ flags = struct.pack('<I', type1_flags)
+ Workstation = gethostname().upper().encode('ascii')
+ user_parts = user.split('\\', 1)
+ DomainName = user_parts[0].upper().encode('ascii')
+ EncryptedRandomSessionKey = ""
+
+ WorkstationLen = struct.pack('<H', len(Workstation))
+ WorkstationMaxLen = struct.pack('<H', len(Workstation))
+ WorkstationBufferOffset = struct.pack('<I', Payload_start)
+ Payload_start += len(Workstation)
+ DomainNameLen = struct.pack('<H', len(DomainName))
+ DomainNameMaxLen = struct.pack('<H', len(DomainName))
+ DomainNameBufferOffset = struct.pack('<I',Payload_start)
+ Payload_start += len(DomainName)
+ ProductMajorVersion = struct.pack('<B', 5)
+ ProductMinorVersion = struct.pack('<B', 1)
+ ProductBuild = struct.pack('<H', 2600)
+ VersionReserved1 = struct.pack('<B', 0)
+ VersionReserved2 = struct.pack('<B', 0)
+ VersionReserved3 = struct.pack('<B', 0)
+ NTLMRevisionCurrent = struct.pack('<B', 15)
+
+ msg1 = protocol + type + flags + \
+ DomainNameLen + DomainNameMaxLen + DomainNameBufferOffset + \
+ WorkstationLen + WorkstationMaxLen + WorkstationBufferOffset + \
+ ProductMajorVersion + ProductMinorVersion + ProductBuild + \
+ VersionReserved1 + VersionReserved2 + VersionReserved3 + NTLMRevisionCurrent
+ assert BODY_LENGTH==len(msg1), "BODY_LENGTH: %d != msg1: %d" % (BODY_LENGTH,len(msg1))
+ msg1 += Workstation + DomainName
+ msg1 = base64.encodestring(msg1)
+ msg1 = string.replace(msg1, '\n', '')
+ return msg1
+
+def parse_NTLM_CHALLENGE_MESSAGE(msg2):
+ ""
+ msg2 = base64.decodestring(msg2)
+ Signature = msg2[0:8]
+ msg_type = struct.unpack("<I",msg2[8:12])[0]
+ assert(msg_type==2)
+ TargetNameLen = struct.unpack("<H",msg2[12:14])[0]
+ TargetNameMaxLen = struct.unpack("<H",msg2[14:16])[0]
+ TargetNameOffset = struct.unpack("<I",msg2[16:20])[0]
+ TargetName = msg2[TargetNameOffset:TargetNameOffset+TargetNameMaxLen]
+ NegotiateFlags = struct.unpack("<I",msg2[20:24])[0]
+ ServerChallenge = msg2[24:32]
+ Reserved = msg2[32:40]
+ TargetInfoLen = struct.unpack("<H",msg2[40:42])[0]
+ TargetInfoMaxLen = struct.unpack("<H",msg2[42:44])[0]
+ TargetInfoOffset = struct.unpack("<I",msg2[44:48])[0]
+ TargetInfo = msg2[TargetInfoOffset:TargetInfoOffset+TargetInfoLen]
+ i=0
+ TimeStamp = '\0'*8
+ while(i<TargetInfoLen):
+ AvId = struct.unpack("<H",TargetInfo[i:i+2])[0]
+ AvLen = struct.unpack("<H",TargetInfo[i+2:i+4])[0]
+ AvValue = TargetInfo[i+4:i+4+AvLen]
+ i = i+4+AvLen
+ if AvId == NTLM_MsvAvTimestamp:
+ TimeStamp = AvValue
+ #~ print AvId, AvValue.decode('utf-16')
+ return (ServerChallenge, NegotiateFlags)
+
+def create_NTLM_AUTHENTICATE_MESSAGE(nonce, user, domain, password, NegotiateFlags):
+ ""
+ is_unicode = NegotiateFlags & NTLM_NegotiateUnicode
+ is_NegotiateExtendedSecurity = NegotiateFlags & NTLM_NegotiateExtendedSecurity
+
+ flags = struct.pack('<I',NTLM_TYPE2_FLAGS)
+
+ BODY_LENGTH = 72
+ Payload_start = BODY_LENGTH # in bytes
+
+ Workstation = gethostname().upper()
+ DomainName = domain.upper()
+ UserName = user
+ EncryptedRandomSessionKey = ""
+ if is_unicode:
+ Workstation = Workstation.encode('utf-16-le')
+ DomainName = DomainName.encode('utf-16-le')
+ UserName = UserName.encode('utf-16-le')
+ EncryptedRandomSessionKey = EncryptedRandomSessionKey.encode('utf-16-le')
+ LmChallengeResponse = calc_resp(create_LM_hashed_password_v1(password), nonce)
+ NtChallengeResponse = calc_resp(create_NT_hashed_password_v1(password), nonce)
+
+ if is_NegotiateExtendedSecurity:
+ pwhash = create_NT_hashed_password_v1(password, UserName, DomainName)
+ ClientChallenge = ""
+ for i in range(8):
+ ClientChallenge+= chr(random.getrandbits(8))
+ (NtChallengeResponse, LmChallengeResponse) = ntlm2sr_calc_resp(pwhash, nonce, ClientChallenge) #='\x39 e3 f4 cd 59 c5 d8 60')
+ Signature = 'NTLMSSP\0'
+ MessageType = struct.pack('<I',3) #type 3
+
+ DomainNameLen = struct.pack('<H', len(DomainName))
+ DomainNameMaxLen = struct.pack('<H', len(DomainName))
+ DomainNameOffset = struct.pack('<I', Payload_start)
+ Payload_start += len(DomainName)
+
+ UserNameLen = struct.pack('<H', len(UserName))
+ UserNameMaxLen = struct.pack('<H', len(UserName))
+ UserNameOffset = struct.pack('<I', Payload_start)
+ Payload_start += len(UserName)
+
+ WorkstationLen = struct.pack('<H', len(Workstation))
+ WorkstationMaxLen = struct.pack('<H', len(Workstation))
+ WorkstationOffset = struct.pack('<I', Payload_start)
+ Payload_start += len(Workstation)
+
+ LmChallengeResponseLen = struct.pack('<H', len(LmChallengeResponse))
+ LmChallengeResponseMaxLen = struct.pack('<H', len(LmChallengeResponse))
+ LmChallengeResponseOffset = struct.pack('<I', Payload_start)
+ Payload_start += len(LmChallengeResponse)
+
+ NtChallengeResponseLen = struct.pack('<H', len(NtChallengeResponse))
+ NtChallengeResponseMaxLen = struct.pack('<H', len(NtChallengeResponse))
+ NtChallengeResponseOffset = struct.pack('<I', Payload_start)
+ Payload_start += len(NtChallengeResponse)
+
+ EncryptedRandomSessionKeyLen = struct.pack('<H', len(EncryptedRandomSessionKey))
+ EncryptedRandomSessionKeyMaxLen = struct.pack('<H', len(EncryptedRandomSessionKey))
+ EncryptedRandomSessionKeyOffset = struct.pack('<I',Payload_start)
+ Payload_start += len(EncryptedRandomSessionKey)
+ NegotiateFlags = flags
+
+ ProductMajorVersion = struct.pack('<B', 5)
+ ProductMinorVersion = struct.pack('<B', 1)
+ ProductBuild = struct.pack('<H', 2600)
+ VersionReserved1 = struct.pack('<B', 0)
+ VersionReserved2 = struct.pack('<B', 0)
+ VersionReserved3 = struct.pack('<B', 0)
+ NTLMRevisionCurrent = struct.pack('<B', 15)
+
+ MIC = struct.pack('<IIII',0,0,0,0)
+ msg3 = Signature + MessageType + \
+ LmChallengeResponseLen + LmChallengeResponseMaxLen + LmChallengeResponseOffset + \
+ NtChallengeResponseLen + NtChallengeResponseMaxLen + NtChallengeResponseOffset + \
+ DomainNameLen + DomainNameMaxLen + DomainNameOffset + \
+ UserNameLen + UserNameMaxLen + UserNameOffset + \
+ WorkstationLen + WorkstationMaxLen + WorkstationOffset + \
+ EncryptedRandomSessionKeyLen + EncryptedRandomSessionKeyMaxLen + EncryptedRandomSessionKeyOffset + \
+ NegotiateFlags + \
+ ProductMajorVersion + ProductMinorVersion + ProductBuild + \
+ VersionReserved1 + VersionReserved2 + VersionReserved3 + NTLMRevisionCurrent
+ assert BODY_LENGTH==len(msg3), "BODY_LENGTH: %d != msg3: %d" % (BODY_LENGTH,len(msg3))
+ Payload = DomainName + UserName + Workstation + LmChallengeResponse + NtChallengeResponse + EncryptedRandomSessionKey
+ msg3 += Payload
+ msg3 = base64.encodestring(msg3)
+ msg3 = string.replace(msg3, '\n', '')
+ return msg3
+
+def calc_resp(password_hash, server_challenge):
+ """calc_resp generates the LM response given a 16-byte password hash and the
+ challenge from the Type-2 message.
+ @param password_hash
+ 16-byte password hash
+ @param server_challenge
+ 8-byte challenge from Type-2 message
+ returns
+ 24-byte buffer to contain the LM response upon return
+ """
+ # padding with zeros to make the hash 21 bytes long
+ password_hash = password_hash + '\0' * (21 - len(password_hash))
+ return des_encrypt(password_hash[ 0: 7], server_challenge[0:8]) + \
+ des_encrypt(password_hash[ 7:14], server_challenge[0:8]) + \
+ des_encrypt(password_hash[14:21], server_challenge[0:8])
+
+def ComputeResponse(ResponseKeyNT, ResponseKeyLM, ServerChallenge, ServerName, ClientChallenge='\xaa'*8, Time='\0'*8):
+ LmChallengeResponse = hmac.new(ResponseKeyLM, ServerChallenge+ClientChallenge).digest() + ClientChallenge
+
+ Responserversion = '\x01'
+ HiResponserversion = '\x01'
+ temp = Responserversion + HiResponserversion + '\0'*6 + Time + ClientChallenge + '\0'*4 + ServerChallenge + '\0'*4
+ NTProofStr = hmac.new(ResponseKeyNT, ServerChallenge + temp).digest()
+ NtChallengeResponse = NTProofStr + temp
+
+ SessionBaseKey = hmac.new(ResponseKeyNT, NTProofStr).digest()
+ return (NtChallengeResponse, LmChallengeResponse)
+
+def ntlm2sr_calc_resp(ResponseKeyNT, ServerChallenge, ClientChallenge='\xaa'*8):
+ LmChallengeResponse = ClientChallenge + '\0'*16
+ sess = hashlib.md5(ServerChallenge+ClientChallenge).digest()
+ NtChallengeResponse = calc_resp(ResponseKeyNT, sess[0:8])
+ return (NtChallengeResponse, LmChallengeResponse)
+
+def create_LM_hashed_password_v1(passwd):
+ "setup LanManager password"
+ "create LanManager hashed password"
+
+ # fix the password length to 14 bytes
+ passwd = string.upper(passwd)
+ lm_pw = passwd + '\0' * (14 - len(passwd))
+ lm_pw = passwd[0:14]
+
+ # do hash
+ magic_str = "KGS!@#$%" # page 57 in [MS-NLMP]
+
+ return des_encrypt(lm_pw[0:7], magic_str) + des_encrypt(lm_pw[7:14], magic_str)
+
+def create_NT_hashed_password_v1(passwd, user=None, domain=None):
+ "create NT hashed password"
+ digest = hashlib.new('md4', passwd.encode('utf-16le')).digest()
+ return digest
+
+def create_NT_hashed_password_v2(passwd, user, domain):
+ "create NT hashed password"
+ digest = create_NT_hashed_password_v1(passwd)
+
+ return hmac.new(digest, (user.upper()+domain).encode('utf-16le')).digest()
+ return digest
+
+def create_sessionbasekey(password):
+ return hashlib.new('md4', create_NT_hashed_password_v1(password)).digest()
+
+if __name__ == "__main__":
+ def ByteToHex( byteStr ):
+ """
+ Convert a byte string to it's hex string representation e.g. for output.
+ """
+ return ' '.join( [ "%02X" % ord( x ) for x in byteStr ] )
+
+ def HexToByte( hexStr ):
+ """
+ Convert a string hex byte values into a byte string. The Hex Byte values may
+ or may not be space separated.
+ """
+ bytes = []
+
+ hexStr = ''.join( hexStr.split(" ") )
+
+ for i in range(0, len(hexStr), 2):
+ bytes.append( chr( int (hexStr[i:i+2], 16 ) ) )
+
+ return ''.join( bytes )
+
+ ServerChallenge = HexToByte("01 23 45 67 89 ab cd ef")
+ ClientChallenge = '\xaa'*8
+ Time = '\x00'*8
+ Workstation = "COMPUTER".encode('utf-16-le')
+ ServerName = "Server".encode('utf-16-le')
+ User = "User"
+ Domain = "Domain"
+ Password = "Password"
+ RandomSessionKey = '\55'*16
+ assert HexToByte("e5 2c ac 67 41 9a 9a 22 4a 3b 10 8f 3f a6 cb 6d") == create_LM_hashed_password_v1(Password) # [MS-NLMP] page 72
+ assert HexToByte("a4 f4 9c 40 65 10 bd ca b6 82 4e e7 c3 0f d8 52") == create_NT_hashed_password_v1(Password) # [MS-NLMP] page 73
+ assert HexToByte("d8 72 62 b0 cd e4 b1 cb 74 99 be cc cd f1 07 84") == create_sessionbasekey(Password)
+ assert HexToByte("67 c4 30 11 f3 02 98 a2 ad 35 ec e6 4f 16 33 1c 44 bd be d9 27 84 1f 94") == calc_resp(create_NT_hashed_password_v1(Password), ServerChallenge)
+ assert HexToByte("98 de f7 b8 7f 88 aa 5d af e2 df 77 96 88 a1 72 de f1 1c 7d 5c cd ef 13") == calc_resp(create_LM_hashed_password_v1(Password), ServerChallenge)
+
+ (NTLMv1Response,LMv1Response) = ntlm2sr_calc_resp(create_NT_hashed_password_v1(Password), ServerChallenge, ClientChallenge)
+ assert HexToByte("aa aa aa aa aa aa aa aa 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00") == LMv1Response # [MS-NLMP] page 75
+ assert HexToByte("75 37 f8 03 ae 36 71 28 ca 45 82 04 bd e7 ca f8 1e 97 ed 26 83 26 72 32") == NTLMv1Response
+
+ assert HexToByte("0c 86 8a 40 3b fd 7a 93 a3 00 1e f2 2e f0 2e 3f") == create_NT_hashed_password_v2(Password, User, Domain) # [MS-NLMP] page 76
+ ResponseKeyLM = ResponseKeyNT = create_NT_hashed_password_v2(Password, User, Domain)
+ (NTLMv2Response,LMv2Response) = ComputeResponse(ResponseKeyNT, ResponseKeyLM, ServerChallenge, ServerName, ClientChallenge, Time)
+ assert HexToByte("86 c3 50 97 ac 9c ec 10 25 54 76 4a 57 cc cc 19 aa aa aa aa aa aa aa aa") == LMv2Response # [MS-NLMP] page 76
+
+ # expected failure
+ # According to the spec in section '3.3.2 NTLM v2 Authentication' the NTLMv2Response should be longer than the value given on page 77 (this suggests a mistake in the spec)
+ #~ assert HexToByte("68 cd 0a b8 51 e5 1c 96 aa bc 92 7b eb ef 6a 1c") == NTLMv2Response, "\nExpected: 68 cd 0a b8 51 e5 1c 96 aa bc 92 7b eb ef 6a 1c\nActual: %s" % ByteToHex(NTLMv2Response) # [MS-NLMP] page 77
diff --git a/virt-who.8 b/virt-who.8
index f26175e..4b2c283 100644
--- a/virt-who.8
+++ b/virt-who.8
@@ -2,7 +2,7 @@
.SH NAME
virt-who - Agent for reporting virtual guest IDs to Subscription Asset Manager.
.SH SYNOPSIS
-virt-who [-d] [-i INTERVAL] [-b] [-o] [--libvirt|--vdsm|--esx|--rhevm]
+virt-who [-d] [-i INTERVAL] [-b] [-o] [--libvirt|--vdsm|--esx|--rhevm|--hyperv]
.SH OPTIONS
.TP
\fB\-h\fR, \fB\-\-help\fR
@@ -31,6 +31,9 @@ Register ESX machines using vCenter
.TP
\fB\-\-rhevm\fR
Register guests using RHEV\-M
+.TP
+\fB\-\-hyperv\fR
+Register guests using Hyper\-V
.IP
.SS vCenter/ESX options
.IP
@@ -69,6 +72,25 @@ Username for connecting to RHEV\-M
.TP
\fB\-\-rhevm\-password\fR=\fIPASSWORD\fR
Password for connecting to RHEV\-M
+.IP
+.SS Hyper\-V options
+.IP
+Use this options with \fB\-\-hyperv\fR
+.TP
+\fB\-\-hyperv\-owner\fR=\fIOWNER\fR
+Organization who has purchased subscriptions of the products
+.TP
+\fB\-\-hyperv\-env\fR=\fIENV\fR
+Environment where the Hyper\-V belongs to
+.TP
+\fB\-\-hyperv\-server\fR=\fISERVER\fR
+URL of the Hyper\-V server to connect to
+.TP
+\fB\-\-hyperv\-username\fR=\fIUSERNAME\fR
+Username for connecting to Hyper\-V
+.TP
+\fB\-\-hyperv\-password\fR=\fIPASSWORD\fR
+Password for connecting to Hyper\-V
.PP
.SH ENVIRONMENT
virt-who also reads environmental variables. They have the same name as command line arguments but upper-cased, with underscore instead of dash and prefixed with VIRTWHO_ (e.g. VIRTWHO_ONE_SHOT). Empty variables are considered as disabled, non-empty as enabled
@@ -127,6 +149,13 @@ Use ESX (vCenter) as virtualization backend and specify option required to conne
Use RHEV-M as virtualization backend and specify option required to connect to RHEV-M server.
+.TP
+4. Hyper-V
+
+# virt-who --hyperv --hyperv-owner=HYPERV_OWNER --hyperv-env=HYPERV_ENV --hyperv-server=HYPERV_SERVER --hyperv-username=HYPERV_USERNAME --hyperv-password=HYPERV_PASSWORD
+
+Use Hyper-V as virtualization backend and specify option required to connect to Hyper-V server.
+
.SH LOGGING
virt-who always writes error output to file /var/log/rhsm/rhsm.log. In all modes, excluding background ("-b"), it writes same output also to the standard error output.
diff --git a/virt-who.conf b/virt-who.conf
index 043b695..2c117d2 100644
--- a/virt-who.conf
+++ b/virt-who.conf
@@ -18,7 +18,7 @@ VIRTWHO_DEBUG=0
# configuration.
#VIRTWHO_INTERVAL=0
-# virt-who mode, enable only one option from following 4:
+# virt-who mode, enable only one option from following 5:
# Use libvirt to list virtual guests [default]
#VIRTWHO_LIBVIRT=1
# Use vdsm to list virtual guests
@@ -27,6 +27,8 @@ VIRTWHO_DEBUG=0
#VIRTWHO_ESX=0
# Register guests using RHEV-M
#VIRTWHO_RHEVM=0
+# Register guest using Hyper-V
+#VIRTWHO_HYPERV=0
# Option for ESX mode
#VIRTWHO_ESX_OWNER=
@@ -41,3 +43,10 @@ VIRTWHO_DEBUG=0
#VIRTWHO_RHEVM_SERVER=
#VIRTWHO_RHEVM_USERNAME=
#VIRTWHO_RHEVM_PASSWORD=
+
+# Options for HYPER-V mode
+#VIRTWHO_HYPERV_OWNER=
+#VIRTWHO_HYPERV_ENV=
+#VIRTWHO_HYPERV_SERVER=
+#VIRTWHO_HYPERV_USERNAME=
+#VIRTWHO_HYPERV_PASSWORD=
diff --git a/virt-who.py b/virt-who.py
index 7b15f14..d121e80 100644
--- a/virt-who.py
+++ b/virt-who.py
@@ -28,6 +28,7 @@ from virt import Virt, VirtError
from vdsm import VDSM
from vsphere import VSphere
from rhevm import RHEVM
+from hyperv import HyperV
from event import virEventLoopPureStart
from subscriptionmanager import SubscriptionManager, SubscriptionManagerError
@@ -98,6 +99,8 @@ class VirtWho(object):
self.tryRegisterEventCallback()
elif self.options.virtType == "rhevm":
self.virt = RHEVM(self.logger, self.options.server, self.options.username, self.options.password)
+ elif self.options.virtType == "hyperv":
+ self.virt = HyperV(self.logger, self.options.server, self.options.username, self.options.password)
else:
# ESX
self.virt = VSphere(self.logger, self.options.server, self.options.username, self.options.password)
@@ -171,7 +174,7 @@ class VirtWho(object):
return False
try:
- if self.options.virtType not in ["esx", "rhevm"]:
+ if self.options.virtType not in ["esx", "rhevm", "hyperv"]:
virtualGuests = self.virt.listDomains()
else:
virtualGuests = self.virt.getHostGuestMapping()
@@ -187,7 +190,7 @@ class VirtWho(object):
return False
try:
- if self.options.virtType not in ["esx", "rhevm"]:
+ if self.options.virtType not in ["esx", "rhevm", "hyperv"]:
self.subscriptionManager.sendVirtGuests(virtualGuests)
else:
result = self.subscriptionManager.hypervisorCheckIn(self.options.owner, self.options.env, virtualGuests)
@@ -325,6 +328,7 @@ def main():
parser.add_option("--vdsm", action="store_const", dest="virtType", const="vdsm", help="Use vdsm to list virtual guests")
parser.add_option("--esx", action="store_const", dest="virtType", const="esx", help="Register ESX machines using vCenter")
parser.add_option("--rhevm", action="store_const", dest="virtType", const="rhevm", help="Register guests using RHEV-M")
+ parser.add_option("--hyperv", action="store_const", dest="virtType", const="hyperv", help="Register guests using Hyper-V")
esxGroup = OptionGroup(parser, "vCenter/ESX options", "Use this options with --esx")
esxGroup.add_option("--esx-owner", action="store", dest="owner", default="", help="Organization who has purchased subscriptions of the products")
@@ -342,6 +346,14 @@ def main():
rhevmGroup.add_option("--rhevm-password", action="store", dest="password", default="", help="Password for connecting to RHEV-M")
parser.add_option_group(rhevmGroup)
+ hypervGroup = OptionGroup(parser, "RHEV-M options", "Use this options with --hyperv")
+ hypervGroup.add_option("--hyperv-owner", action="store", dest="owner", default="", help="Organization who has purchased subscriptions of the products")
+ hypervGroup.add_option("--hyperv-env", action="store", dest="env", default="", help="Environment where the Hyper-V belongs to")
+ hypervGroup.add_option("--hyperv-server", action="store", dest="server", default="", help="URL of the Hyper-V server to connect to")
+ hypervGroup.add_option("--hyperv-username", action="store", dest="username", default="", help="Username for connecting to Hyper-V")
+ hypervGroup.add_option("--hyperv-password", action="store", dest="password", default="", help="Password for connecting to Hyper-V")
+ parser.add_option_group(hypervGroup)
+
(options, args) = parser.parse_args()
# Handle enviromental variables
@@ -379,6 +391,11 @@ def main():
if env in ["1", "true"]:
options.virtType = "rhevm"
+ env = os.getenv("VIRTWHO_HYPERV", "0").strip().lower()
+ if env in ["1", "true"]:
+ options.virtType = "hyperv"
+
+
def checkEnv(variable, option, name):
"""
If `option` is empty, check enviromental `variable` and return its value.
@@ -407,6 +424,14 @@ def main():
if len(options.password) == 0:
options.password = os.getenv("VIRTWHO_RHEVM_PASSWORD", "")
+ if options.virtType == "hyperv":
+ options.owner = checkEnv("VIRTWHO_HYPERV_OWNER", options.owner, "owner")
+ options.env = checkEnv("VIRTWHO_HYPERV_ENV", options.env, "env")
+ options.server = checkEnv("VIRTWHO_HYPERV_SERVER", options.server, "server")
+ options.username = checkEnv("VIRTWHO_HYPERV_USERNAME", options.username, "username")
+ if len(options.password) == 0:
+ options.password = os.getenv("VIRTWHO_HYPERV_PASSWORD", "")
+
if options.interval < 0:
logger.warning("Interval is not positive number, ignoring")
options.interval = 0
11 years, 6 months
[virt-who] Create PID file as soon as possible
by Radek Novacek
commit ac15c128524cd317623044d7fbaedb4eef3b557e
Author: Radek Novacek <rnovacek(a)redhat.com>
Date: Thu Oct 4 12:06:36 2012 +0200
Create PID file as soon as possible
PID file was created too late causing service stop to be unsuccessfull
if call too soon after service start.
Now PID file is create immediatelly when service is started and
recreated after fork.
virt-who.py | 9 +++++----
1 files changed, 5 insertions(+), 4 deletions(-)
---
diff --git a/virt-who.py b/virt-who.py
index 0c8babf..7b15f14 100644
--- a/virt-who.py
+++ b/virt-who.py
@@ -286,7 +286,7 @@ def daemonize(debugMode):
os.chdir("/")
return True
-def createPidFile(logger):
+def createPidFile(logger=None):
atexit.register(cleanup)
signal.signal(signal.SIGINT, cleanup)
signal.signal(signal.SIGTERM, cleanup)
@@ -297,7 +297,8 @@ def createPidFile(logger):
f.write("%d" % os.getpid())
f.close()
except Exception, e:
- logger.error("Unable to create pid file: %s" % str(e))
+ if logger is not None:
+ logger.error("Unable to create pid file: %s" % str(e))
def cleanup(sig=None, stack=None):
try:
@@ -312,6 +313,7 @@ def main():
if os.access(PIDFILE, os.F_OK):
print >>sys.stderr, "virt-who seems to be already running. If not, remove %s" % PIDFILE
sys.exit(1)
+ createPidFile()
parser = OptionParserEpilog(description="Agent for reporting virtual guest IDs to subscription-manager",
epilog="virt-who also reads enviromental variables. They have the same name as command line arguments but uppercased, with underscore instead of dash and prefixed with VIRTWHO_ (e.g. VIRTWHO_ONE_SHOT). Empty variables are considered as disabled, non-empty as enabled")
@@ -425,6 +427,7 @@ def main():
# Do a double-fork and other daemon initialization
if not daemonize(options.debug):
logger.error("Unable to fork, continuing in foreground")
+ createPidFile(logger)
if not options.oneshot:
if options.background and options.virtType == "libvirt":
@@ -444,8 +447,6 @@ def main():
except Exception:
pass
- createPidFile(logger)
-
logger.debug("Virt-who is running in %s mode" % options.virtType)
if options.oneshot:
11 years, 6 months