Merge "Split xenapi agent code out to nova.virt.xenapi.agent"
This commit is contained in:
@@ -40,6 +40,7 @@ from nova.tests import fake_network
|
||||
from nova.tests import fake_utils
|
||||
import nova.tests.image.fake as fake_image
|
||||
from nova.tests.xenapi import stubs
|
||||
from nova.virt.xenapi import agent
|
||||
from nova.virt.xenapi import driver as xenapi_conn
|
||||
from nova.virt.xenapi import fake as xenapi_fake
|
||||
from nova.virt.xenapi import vm_utils
|
||||
@@ -862,8 +863,8 @@ class XenAPIDiffieHellmanTestCase(test.TestCase):
|
||||
"""Unit tests for Diffie-Hellman code."""
|
||||
def setUp(self):
|
||||
super(XenAPIDiffieHellmanTestCase, self).setUp()
|
||||
self.alice = vmops.SimpleDH()
|
||||
self.bob = vmops.SimpleDH()
|
||||
self.alice = agent.SimpleDH()
|
||||
self.bob = agent.SimpleDH()
|
||||
|
||||
def test_shared(self):
|
||||
alice_pub = self.alice.get_public()
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 2010 Citrix Systems, Inc.
|
||||
# Copyright 2010-2012 OpenStack LLC.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import binascii
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from nova import flags
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
xenapi_agent_opts = [
|
||||
cfg.IntOpt('agent_version_timeout',
|
||||
default=300,
|
||||
help='number of seconds to wait for agent '
|
||||
'to be fully operational'),
|
||||
]
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
FLAGS.register_opts(xenapi_agent_opts)
|
||||
|
||||
|
||||
def _call_agent(session, instance, vm_ref, method, addl_args=None):
|
||||
"""Abstracts out the interaction with the agent xenapi plugin."""
|
||||
if addl_args is None:
|
||||
addl_args = {}
|
||||
|
||||
vm_rec = session.call_xenapi("VM.get_record", vm_ref)
|
||||
|
||||
args = {
|
||||
'id': str(uuid.uuid4()),
|
||||
'dom_id': vm_rec['domid'],
|
||||
}
|
||||
args.update(addl_args)
|
||||
|
||||
try:
|
||||
ret = session.call_plugin('agent', method, args)
|
||||
except session.XenAPI.Failure, e:
|
||||
err_msg = e.details[-1].splitlines()[-1]
|
||||
if 'TIMEOUT:' in err_msg:
|
||||
LOG.error(_('TIMEOUT: The call to %(method)s timed out. '
|
||||
'args=%(args)r'), locals(), instance=instance)
|
||||
return {'returncode': 'timeout', 'message': err_msg}
|
||||
elif 'NOT IMPLEMENTED:' in err_msg:
|
||||
LOG.error(_('NOT IMPLEMENTED: The call to %(method)s is not'
|
||||
' supported by the agent. args=%(args)r'),
|
||||
locals(), instance=instance)
|
||||
return {'returncode': 'notimplemented', 'message': err_msg}
|
||||
else:
|
||||
LOG.error(_('The call to %(method)s returned an error: %(e)s. '
|
||||
'args=%(args)r'), locals(), instance=instance)
|
||||
return {'returncode': 'error', 'message': err_msg}
|
||||
return None
|
||||
|
||||
if isinstance(ret, dict):
|
||||
return ret
|
||||
try:
|
||||
return jsonutils.loads(ret)
|
||||
except TypeError:
|
||||
LOG.error(_('The agent call to %(method)s returned an invalid'
|
||||
' response: %(ret)r. path=%(path)s; args=%(args)r'),
|
||||
locals(), instance=instance)
|
||||
return {'returncode': 'error',
|
||||
'message': 'unable to deserialize response'}
|
||||
|
||||
|
||||
def _get_agent_version(session, instance, vm_ref):
|
||||
resp = _call_agent(session, instance, vm_ref, 'version')
|
||||
if resp['returncode'] != '0':
|
||||
LOG.error(_('Failed to query agent version: %(resp)r'),
|
||||
locals(), instance=instance)
|
||||
return None
|
||||
|
||||
# Some old versions of the Windows agent have a trailing \\r\\n
|
||||
# (ie CRLF escaped) for some reason. Strip that off.
|
||||
return resp['message'].replace('\\r\\n', '')
|
||||
|
||||
|
||||
def get_agent_version(session, instance, vm_ref):
|
||||
"""Get the version of the agent running on the VM instance."""
|
||||
|
||||
# The agent can be slow to start for a variety of reasons. On Windows,
|
||||
# it will generally perform a setup process on first boot that can
|
||||
# take a couple of minutes and then reboot. On Linux, the system can
|
||||
# also take a while to boot. So we need to be more patient than
|
||||
# normal as well as watch for domid changes
|
||||
|
||||
expiration = time.time() + FLAGS.agent_version_timeout
|
||||
while time.time() < expiration:
|
||||
ret = _get_agent_version(session, instance, vm_ref)
|
||||
if ret:
|
||||
return ret
|
||||
|
||||
LOG.info(_('Reached maximum time attempting to query agent version'),
|
||||
instance=instance)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def agent_update(session, instance, vm_ref, url, md5sum):
|
||||
"""Update agent on the VM instance."""
|
||||
|
||||
# Send the encrypted password
|
||||
args = {'url': url, 'md5sum': md5sum}
|
||||
resp = _call_agent(session, instance, vm_ref, 'agentupdate', args)
|
||||
if resp['returncode'] != '0':
|
||||
LOG.error(_('Failed to update agent: %(resp)r'), locals(),
|
||||
instance=instance)
|
||||
return None
|
||||
return resp['message']
|
||||
|
||||
|
||||
def set_admin_password(session, instance, vm_ref, new_pass):
|
||||
"""Set the root/admin password on the VM instance.
|
||||
|
||||
This is done via an agent running on the VM. Communication between nova
|
||||
and the agent is done via writing xenstore records. Since communication
|
||||
is done over the XenAPI RPC calls, we need to encrypt the password.
|
||||
We're using a simple Diffie-Hellman class instead of a more advanced
|
||||
library (such as M2Crypto) for compatibility with the agent code.
|
||||
"""
|
||||
dh = SimpleDH()
|
||||
|
||||
# Exchange keys
|
||||
args = {'pub': str(dh.get_public())}
|
||||
resp = _call_agent(session, instance, vm_ref, 'key_init', args)
|
||||
|
||||
# Successful return code from key_init is 'D0'
|
||||
if resp['returncode'] != 'D0':
|
||||
msg = _('Failed to exchange keys: %(resp)r') % locals()
|
||||
LOG.error(msg, instance=instance)
|
||||
raise Exception(msg)
|
||||
|
||||
# Some old versions of the Windows agent have a trailing \\r\\n
|
||||
# (ie CRLF escaped) for some reason. Strip that off.
|
||||
agent_pub = int(resp['message'].replace('\\r\\n', ''))
|
||||
dh.compute_shared(agent_pub)
|
||||
|
||||
# Some old versions of Linux and Windows agent expect trailing \n
|
||||
# on password to work correctly.
|
||||
enc_pass = dh.encrypt(new_pass + '\n')
|
||||
|
||||
# Send the encrypted password
|
||||
args = {'enc_pass': enc_pass}
|
||||
resp = _call_agent(session, instance, vm_ref, 'password', args)
|
||||
|
||||
# Successful return code from password is '0'
|
||||
if resp['returncode'] != '0':
|
||||
msg = _('Failed to update password: %(resp)r') % locals()
|
||||
LOG.error(msg, instance=instance)
|
||||
raise Exception(msg)
|
||||
|
||||
return resp['message']
|
||||
|
||||
|
||||
def inject_file(session, instance, vm_ref, path, contents):
|
||||
# Files/paths must be base64-encoded for transmission to agent
|
||||
b64_path = base64.b64encode(path)
|
||||
b64_contents = base64.b64encode(contents)
|
||||
|
||||
args = {'b64_path': b64_path, 'b64_contents': b64_contents}
|
||||
|
||||
# If the agent doesn't support file injection, a NotImplementedError
|
||||
# will be raised with the appropriate message.
|
||||
resp = _call_agent(session, instance, vm_ref, 'inject_file', args)
|
||||
if resp['returncode'] != '0':
|
||||
LOG.error(_('Failed to inject file: %(resp)r'), locals(),
|
||||
instance=instance)
|
||||
return None
|
||||
|
||||
return resp['message']
|
||||
|
||||
|
||||
def resetnetwork(session, instance, vm_ref):
|
||||
"""Calls resetnetwork method in agent."""
|
||||
_call_agent(session, instance, vm_ref, 'resetnetwork')
|
||||
|
||||
|
||||
class SimpleDH(object):
|
||||
"""
|
||||
This class wraps all the functionality needed to implement
|
||||
basic Diffie-Hellman-Merkle key exchange in Python. It features
|
||||
intelligent defaults for the prime and base numbers needed for the
|
||||
calculation, while allowing you to supply your own. It requires that
|
||||
the openssl binary be installed on the system on which this is run,
|
||||
as it uses that to handle the encryption and decryption. If openssl
|
||||
is not available, a RuntimeError will be raised.
|
||||
"""
|
||||
def __init__(self):
|
||||
self._prime = 162259276829213363391578010288127
|
||||
self._base = 5
|
||||
self._public = None
|
||||
self._shared = None
|
||||
self.generate_private()
|
||||
|
||||
def generate_private(self):
|
||||
self._private = int(binascii.hexlify(os.urandom(10)), 16)
|
||||
return self._private
|
||||
|
||||
def get_public(self):
|
||||
self._public = self.mod_exp(self._base, self._private, self._prime)
|
||||
return self._public
|
||||
|
||||
def compute_shared(self, other):
|
||||
self._shared = self.mod_exp(other, self._private, self._prime)
|
||||
return self._shared
|
||||
|
||||
@staticmethod
|
||||
def mod_exp(num, exp, mod):
|
||||
"""Efficient implementation of (num ** exp) % mod"""
|
||||
result = 1
|
||||
while exp > 0:
|
||||
if (exp & 1) == 1:
|
||||
result = (result * num) % mod
|
||||
exp = exp >> 1
|
||||
num = (num * num) % mod
|
||||
return result
|
||||
|
||||
def _run_ssl(self, text, decrypt=False):
|
||||
cmd = ['openssl', 'aes-128-cbc', '-A', '-a', '-pass',
|
||||
'pass:%s' % self._shared, '-nosalt']
|
||||
if decrypt:
|
||||
cmd.append('-d')
|
||||
out, err = utils.execute(*cmd, process_input=text)
|
||||
if err:
|
||||
raise RuntimeError(_('OpenSSL error: %s') % err)
|
||||
return out
|
||||
|
||||
def encrypt(self, text):
|
||||
return self._run_ssl(text).strip('\n')
|
||||
|
||||
def decrypt(self, text):
|
||||
return self._run_ssl(text, decrypt=True)
|
||||
+18
-198
@@ -19,8 +19,6 @@
|
||||
Management class for VM-related functions (spawn, reboot, etc).
|
||||
"""
|
||||
|
||||
import base64
|
||||
import binascii
|
||||
import cPickle as pickle
|
||||
import functools
|
||||
import os
|
||||
@@ -43,6 +41,7 @@ from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import timeutils
|
||||
from nova import utils
|
||||
from nova.virt import driver
|
||||
from nova.virt.xenapi import agent
|
||||
from nova.virt.xenapi import firewall
|
||||
from nova.virt.xenapi import network_utils
|
||||
from nova.virt.xenapi import vm_utils
|
||||
@@ -52,10 +51,6 @@ from nova.virt.xenapi import volume_utils
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
xenapi_vmops_opts = [
|
||||
cfg.IntOpt('agent_version_timeout',
|
||||
default=300,
|
||||
help='number of seconds to wait for agent '
|
||||
'to be fully operational'),
|
||||
cfg.IntOpt('xenapi_running_timeout',
|
||||
default=60,
|
||||
help='number of seconds to wait for instance '
|
||||
@@ -508,7 +503,7 @@ class VMOps(object):
|
||||
# Update agent, if necessary
|
||||
# This also waits until the agent starts
|
||||
LOG.debug(_("Querying agent version"), instance=instance)
|
||||
version = self._get_agent_version(instance)
|
||||
version = agent.get_agent_version(self._session, instance, vm_ref)
|
||||
if version:
|
||||
LOG.info(_('Instance agent version: %s'), version,
|
||||
instance=instance)
|
||||
@@ -517,8 +512,8 @@ class VMOps(object):
|
||||
cmp_version(version, agent_build['version']) < 0):
|
||||
LOG.info(_('Updating Agent to %s'), agent_build['version'],
|
||||
instance=instance)
|
||||
self._agent_update(instance, agent_build['url'],
|
||||
agent_build['md5hash'])
|
||||
agent.agent_update(self._session, instance, vm_ref,
|
||||
agent_build['url'], agent_build['md5hash'])
|
||||
|
||||
# if the guest agent is not available, configure the
|
||||
# instance, but skip the admin password configuration
|
||||
@@ -539,17 +534,19 @@ class VMOps(object):
|
||||
for path, contents in instance.injected_files:
|
||||
LOG.debug(_("Injecting file path: '%s'") % path,
|
||||
instance=instance)
|
||||
self.inject_file(instance, path, contents)
|
||||
agent.inject_file(self._session, instance, vm_ref,
|
||||
path, contents)
|
||||
|
||||
admin_password = instance.admin_pass
|
||||
# Set admin password, if necessary
|
||||
if admin_password and not no_agent:
|
||||
LOG.debug(_("Setting admin password"), instance=instance)
|
||||
self.set_admin_password(instance, admin_password)
|
||||
agent.set_admin_password(self._session, instance, vm_ref,
|
||||
admin_password)
|
||||
|
||||
# Reset network config
|
||||
LOG.debug(_("Resetting network"), instance=instance)
|
||||
self.reset_network(instance, vm_ref)
|
||||
agent.resetnetwork(self._session, instance, vm_ref)
|
||||
|
||||
# Set VCPU weight
|
||||
inst_type = db.instance_type_get(ctx, instance.instance_type_id)
|
||||
@@ -871,118 +868,15 @@ class VMOps(object):
|
||||
return
|
||||
raise
|
||||
|
||||
def _get_agent_version(self, instance):
|
||||
"""Get the version of the agent running on the VM instance."""
|
||||
|
||||
# The agent can be slow to start for a variety of reasons. On Windows,
|
||||
# it will generally perform a setup process on first boot that can
|
||||
# take a couple of minutes and then reboot. On Linux, the system can
|
||||
# also take a while to boot. So we need to be more patient than
|
||||
# normal as well as watch for domid changes
|
||||
|
||||
def _call():
|
||||
# Send the encrypted password
|
||||
resp = self._make_agent_call('version', instance)
|
||||
if resp['returncode'] != '0':
|
||||
LOG.error(_('Failed to query agent version: %(resp)r'),
|
||||
locals(), instance=instance)
|
||||
return None
|
||||
# Some old versions of the Windows agent have a trailing \\r\\n
|
||||
# (ie CRLF escaped) for some reason. Strip that off.
|
||||
return resp['message'].replace('\\r\\n', '')
|
||||
|
||||
vm_ref = self._get_vm_opaque_ref(instance)
|
||||
vm_rec = self._session.call_xenapi("VM.get_record", vm_ref)
|
||||
|
||||
domid = vm_rec['domid']
|
||||
|
||||
expiration = time.time() + FLAGS.agent_version_timeout
|
||||
while time.time() < expiration:
|
||||
ret = _call()
|
||||
if ret:
|
||||
return ret
|
||||
|
||||
vm_rec = self._session.call_xenapi("VM.get_record", vm_ref)
|
||||
if vm_rec['domid'] != domid:
|
||||
newdomid = vm_rec['domid']
|
||||
LOG.info(_('domid changed from %(domid)s to %(newdomid)s'),
|
||||
locals(), instance=instance)
|
||||
domid = vm_rec['domid']
|
||||
|
||||
return None
|
||||
|
||||
def _agent_update(self, instance, url, md5sum):
|
||||
"""Update agent on the VM instance."""
|
||||
|
||||
# Send the encrypted password
|
||||
args = {'url': url, 'md5sum': md5sum}
|
||||
resp = self._make_agent_call('agentupdate', instance, args)
|
||||
if resp['returncode'] != '0':
|
||||
LOG.error(_('Failed to update agent: %(resp)r'), locals(),
|
||||
instance=instance)
|
||||
return None
|
||||
return resp['message']
|
||||
|
||||
def set_admin_password(self, instance, new_pass):
|
||||
"""Set the root/admin password on the VM instance.
|
||||
|
||||
This is done via an agent running on the VM. Communication between nova
|
||||
and the agent is done via writing xenstore records. Since communication
|
||||
is done over the XenAPI RPC calls, we need to encrypt the password.
|
||||
We're using a simple Diffie-Hellman class instead of the more advanced
|
||||
one in M2Crypto for compatibility with the agent code.
|
||||
|
||||
"""
|
||||
# The simple Diffie-Hellman class is used to manage key exchange.
|
||||
dh = SimpleDH()
|
||||
key_init_args = {'pub': str(dh.get_public())}
|
||||
resp = self._make_agent_call('key_init', instance, key_init_args)
|
||||
# Successful return code from key_init is 'D0'
|
||||
if resp['returncode'] != 'D0':
|
||||
msg = _('Failed to exchange keys: %(resp)r') % locals()
|
||||
LOG.error(msg, instance=instance)
|
||||
raise Exception(msg)
|
||||
# Some old versions of the Windows agent have a trailing \\r\\n
|
||||
# (ie CRLF escaped) for some reason. Strip that off.
|
||||
agent_pub = int(resp['message'].replace('\\r\\n', ''))
|
||||
dh.compute_shared(agent_pub)
|
||||
# Some old versions of Linux and Windows agent expect trailing \n
|
||||
# on password to work correctly.
|
||||
enc_pass = dh.encrypt(new_pass + '\n')
|
||||
# Send the encrypted password
|
||||
password_args = {'enc_pass': enc_pass}
|
||||
resp = self._make_agent_call('password', instance, password_args)
|
||||
# Successful return code from password is '0'
|
||||
if resp['returncode'] != '0':
|
||||
msg = _('Failed to update password: %(resp)r') % locals()
|
||||
LOG.error(msg, instance=instance)
|
||||
raise Exception(msg)
|
||||
return resp['message']
|
||||
"""Set the root/admin password on the VM instance."""
|
||||
vm_ref = self._get_vm_opaque_ref(instance)
|
||||
agent.set_admin_password(self._session, instance, vm_ref, new_pass)
|
||||
|
||||
def inject_file(self, instance, path, contents):
|
||||
"""Write a file to the VM instance.
|
||||
|
||||
The path to which it is to be written and the contents of the file
|
||||
need to be supplied; both will be base64-encoded to prevent errors
|
||||
with non-ASCII characters being transmitted. If the agent does not
|
||||
support file injection, or the user has disabled it, a
|
||||
NotImplementedError will be raised.
|
||||
|
||||
"""
|
||||
# Files/paths must be base64-encoded for transmission to agent
|
||||
b64_path = base64.b64encode(path)
|
||||
b64_contents = base64.b64encode(contents)
|
||||
|
||||
# Need to uniquely identify this request.
|
||||
args = {'b64_path': b64_path, 'b64_contents': b64_contents}
|
||||
# If the agent doesn't support file injection, a NotImplementedError
|
||||
# will be raised with the appropriate message.
|
||||
resp = self._make_agent_call('inject_file', instance, args)
|
||||
if resp['returncode'] != '0':
|
||||
LOG.error(_('Failed to inject file: %(resp)r'), locals(),
|
||||
instance=instance)
|
||||
return None
|
||||
return resp['message']
|
||||
"""Write a file to the VM instance."""
|
||||
vm_ref = self._get_vm_opaque_ref(instance)
|
||||
agent.inject_file(self._session, instance, vm_ref, path, contents)
|
||||
|
||||
def _shutdown(self, instance, vm_ref, hard=True):
|
||||
"""Shutdown an instance."""
|
||||
@@ -1484,9 +1378,10 @@ class VMOps(object):
|
||||
for vif in network_info:
|
||||
self.vif_driver.unplug(instance, vif)
|
||||
|
||||
def reset_network(self, instance, vm_ref=None):
|
||||
def reset_network(self, instance):
|
||||
"""Calls resetnetwork method in agent."""
|
||||
self._make_agent_call('resetnetwork', instance, vm_ref=vm_ref)
|
||||
vm_ref = self._get_vm_opaque_ref(instance)
|
||||
agent.resetnetwork(self._session, instance, vm_ref)
|
||||
|
||||
def inject_hostname(self, instance, vm_ref, hostname):
|
||||
"""Inject the hostname of the instance into the xenstore."""
|
||||
@@ -1507,24 +1402,6 @@ class VMOps(object):
|
||||
vm_ref=vm_ref, path=path,
|
||||
value=jsonutils.dumps(value))
|
||||
|
||||
def _make_agent_call(self, method, instance, args=None, vm_ref=None):
|
||||
"""Abstracts out the interaction with the agent xenapi plugin."""
|
||||
if args is None:
|
||||
args = {}
|
||||
args['id'] = str(uuid.uuid4())
|
||||
ret = self._make_plugin_call('agent', method, instance, vm_ref=vm_ref,
|
||||
**args)
|
||||
if isinstance(ret, dict):
|
||||
return ret
|
||||
try:
|
||||
return jsonutils.loads(ret)
|
||||
except TypeError:
|
||||
LOG.error(_('The agent call to %(method)s returned an invalid'
|
||||
' response: %(ret)r. path=%(path)s; args=%(args)r'),
|
||||
locals(), instance=instance)
|
||||
return {'returncode': 'error',
|
||||
'message': 'unable to deserialize response'}
|
||||
|
||||
def _make_plugin_call(self, plugin, method, instance, vm_ref=None,
|
||||
**addl_args):
|
||||
"""
|
||||
@@ -1586,60 +1463,3 @@ class VMOps(object):
|
||||
"""Removes filters for each VIF of the specified instance."""
|
||||
self.firewall_driver.unfilter_instance(instance_ref,
|
||||
network_info=network_info)
|
||||
|
||||
|
||||
class SimpleDH(object):
|
||||
"""
|
||||
This class wraps all the functionality needed to implement
|
||||
basic Diffie-Hellman-Merkle key exchange in Python. It features
|
||||
intelligent defaults for the prime and base numbers needed for the
|
||||
calculation, while allowing you to supply your own. It requires that
|
||||
the openssl binary be installed on the system on which this is run,
|
||||
as it uses that to handle the encryption and decryption. If openssl
|
||||
is not available, a RuntimeError will be raised.
|
||||
"""
|
||||
def __init__(self):
|
||||
self._prime = 162259276829213363391578010288127
|
||||
self._base = 5
|
||||
self._public = None
|
||||
self._shared = None
|
||||
self.generate_private()
|
||||
|
||||
def generate_private(self):
|
||||
self._private = int(binascii.hexlify(os.urandom(10)), 16)
|
||||
return self._private
|
||||
|
||||
def get_public(self):
|
||||
self._public = self.mod_exp(self._base, self._private, self._prime)
|
||||
return self._public
|
||||
|
||||
def compute_shared(self, other):
|
||||
self._shared = self.mod_exp(other, self._private, self._prime)
|
||||
return self._shared
|
||||
|
||||
@staticmethod
|
||||
def mod_exp(num, exp, mod):
|
||||
"""Efficient implementation of (num ** exp) % mod"""
|
||||
result = 1
|
||||
while exp > 0:
|
||||
if (exp & 1) == 1:
|
||||
result = (result * num) % mod
|
||||
exp = exp >> 1
|
||||
num = (num * num) % mod
|
||||
return result
|
||||
|
||||
def _run_ssl(self, text, decrypt=False):
|
||||
cmd = ['openssl', 'aes-128-cbc', '-A', '-a', '-pass',
|
||||
'pass:%s' % self._shared, '-nosalt']
|
||||
if decrypt:
|
||||
cmd.append('-d')
|
||||
out, err = utils.execute(*cmd, process_input=text)
|
||||
if err:
|
||||
raise RuntimeError(_('OpenSSL error: %s') % err)
|
||||
return out
|
||||
|
||||
def encrypt(self, text):
|
||||
return self._run_ssl(text).strip('\n')
|
||||
|
||||
def decrypt(self, text):
|
||||
return self._run_ssl(text, decrypt=True)
|
||||
|
||||
Reference in New Issue
Block a user