Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] OpenVMS #54731

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 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
21 changes: 21 additions & 0 deletions lib/ansible/executor/module_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
REPLACER_VERSION = b"\"<<ANSIBLE_VERSION>>\""
REPLACER_COMPLEX = b"\"<<INCLUDE_ANSIBLE_MODULE_COMPLEX_ARGS>>\""
REPLACER_WINDOWS = b"# POWERSHELL_COMMON"
REPLACER_VMS = b"\$! DCL_COMMON"
REPLACER_JSONARGS = b"<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>"
REPLACER_SELINUX = b"<<SELINUX_SPECIAL_FILESYSTEMS>>"

Expand Down Expand Up @@ -765,6 +766,9 @@ def _find_module_utils(module_name, b_module_data, module_path, module_args, tas
elif b'from ansible.module_utils.' in b_module_data:
module_style = 'new'
module_substyle = 'python'
elif REPLACER_VMS in b_module_data:
module_style = 'new'
module_substyle = 'dcl'
elif REPLACER_WINDOWS in b_module_data:
module_style = 'new'
module_substyle = 'powershell'
Expand Down Expand Up @@ -940,6 +944,16 @@ def _find_module_utils(module_name, b_module_data, module_path, module_args, tas
)))
b_module_data = output.getvalue()

elif module_substyle == 'dcl':
# Start of DCL support
# shebang needs to start with $!....
shebang = u'$!'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or we can move 'shebang' detection to the shell module (might also help with powershell @nitzmahone @jborean93 ?)

# not sure... what is needed
b_module_data = ""
if become:
display.vvv('DCL: Become unsupported')
become = False

elif module_substyle == 'powershell':
# Powershell/winrm don't actually make use of shebang so we can
# safely set this here. If we let the fallback code handle this
Expand Down Expand Up @@ -1033,6 +1047,13 @@ def modify_module(module_name, module_path, module_args, templar, task_vars=None
b_lines.insert(1, b_ENCODING_STRING)

shebang = to_text(b_shebang, nonstring='passthru', errors='surrogate_or_strict')

# DCL lines start with in regex: $[ \t]*(!|@?[A-Z0-9_]+[: \t]?).*$
# line continuation: lines ending in - except for comments.
# comment ! ... until en of line
elif b_lines[0].startswith(b"$")
interpreter = "dcl"

else:
# No shebang, assume a binary module?
pass
Expand Down
19 changes: 17 additions & 2 deletions lib/ansible/plugins/connection/ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@
version_added: '2.7'
use_tty:
version_added: '2.5'
default: 'yes'
default: 'yes' forced to no for OpenVMS
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont add comments to default values, this breaks default processing

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated.

description: add -tt to ssh commands to force tty allocation
env: [{name: ANSIBLE_SSH_USETTY}]
ini:
Expand Down Expand Up @@ -463,6 +463,12 @@ def __init__(self, *args, **kwargs):
self.always_pipeline_modules = True
self.module_implementation_preferences = ('.ps1', '.exe', '')
self.allow_executable = False
if getattr(self._shell,"_IS_OPENVMS", False):
self._shell_type = 'dcl'
self.has_pipelining = False
self.always_pipeline_modules = False
self.module_implementation_preferences = ('.dcl', '.com', '.exe', '')
self.allow_executable = False

# The connection is created by running ssh/scp/sftp from the exec_command,
# put_file, and fetch_file methods, so we don't need to do any connection
Expand Down Expand Up @@ -1055,6 +1061,9 @@ def _file_transport_command(self, in_path, out_path, sftp_action):
# Windows does not support dd so we cannot use the piped method
if getattr(self._shell, "_IS_WINDOWS", False):
smart_methods.remove('piped')
# OpenVMS does not support dd so we cannot use the piped method
if getattr(self._shell, "_IS_OPENVMS", False):
smart_methods.remove('piped')

# Transfer methods to try
methods = []
Expand Down Expand Up @@ -1174,6 +1183,12 @@ def exec_command(self, cmd, in_data=None, sudoable=True):
# -tt can cause various issues in some environments so allow the user
# to disable it as a troubleshooting method.
use_tty = self.get_option('use_tty')
if getattr(self._shell, "_IS_OPENVMS", False):
# Become method 'runas' is done in the wrapper that is executed,
# need to disable sudoable so the bare_run is not waiting for a
# prompt that will not occur
sudoable = False
use_tty = 'no'

if not in_data and sudoable and use_tty:
args = (ssh_executable, '-tt', self.host, cmd)
Expand Down Expand Up @@ -1243,4 +1258,4 @@ def reset(self):
self.close()

def close(self):
self._connected = False
self._connected = False
111 changes: 111 additions & 0 deletions lib/ansible/plugins/shell/dcl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Copyright (c) 2014, Chris Church <chris@ninemoreminutes.com>
# Copyright (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

from ansible.module_utils.six import text_type
from ansible.module_utils.six.moves import shlex_quote
from ansible.plugins.shell.sh import ShellModule as ShModule

DOCUMENTATION = '''
name: dcl
plugin_type: shell
version_added: ""
short_description: DCL shell OpenVMS COmmand line, no separate command...
description:
- This is to support OpenVMS, ( initialy based on fish)
extends_documentation_fragment:
- shell_common
'''


class ShellModule(ShModule):

# Common shell filenames that this plugin handles
COMPATIBLE_SHELLS = frozenset(('dcl',))
# Family of shells this has. Must match the filename without extension
SHELL_FAMILY = 'dcl'
IS_OPENVMS = True

#?
_SHELL_EMBEDDED_PY_EOL = '\n'
# The next can be done by prepending command with a few extra lines
# $ sts = $STATUS ! keep status
# $ DEFINE/USER SYS$OUTPUT NL: ! these will clobber the dcl status
# $ DEFINE/USER SYS$ERROR NL:
# $ $STATUS = sts ! restor previous command's status
# when generating a script the $ prefix is mandatory, when issuing commands they should be left out.
_SHELL_REDIRECT_ALLNULL = '\nsts = $STATUS\nDEFINE/USER SYS$OUTPUT NL:\nDEFINE/USER SYS$ERROR NL:\n$STATUS = sts'
# The next should be prepended to the command
_SHELL_AND = '\nIF $status THEN '
# Following are unsupported....
_SHELL_OR = ''
_SHELL_SUB_LEFT = ''
_SHELL_SUB_RIGHT = ''
_SHELL_GROUP_LEFT = ''
_SHELL_GROUP_RIGHT = ''

# can only be done by adding assignment before the command...
# NOT AFTER IF ... THEN... Quoting will be an issue....
# strings: "......"
# variable expansion: 'var' (early, before CLI parsing) &var (late, during CLI parsing)
# ''var' when variables are inside a string.
# escaping a " double up.. "what""ever""" will show as: what"ever" when printed...
def env_prefix(self, **kwargs):
env = self.env.copy()
env.update(kwargs)
return ' '.join(['\n$ %s=%s' % (k, shlex_quote(text_type(v))) for k, v in env.items()])

#def build_module_command(self, env_string, shebang, cmd, arg_path=None):
# # don't quote the cmd if it's an empty string, because this will break pipelining mode
# #if cmd.strip() != '':
# # cmd = shlex_quote(cmd)
# #cmd_parts = [env_string.strip(), shebang.replace("#!", "").strip(), cmd]
# #if arg_path is not None:
# # cmd_parts.append(arg_path)
# #new_cmd = " ".join(cmd_parts)
# new_cmd = cmd
# return new_cmd

## needed for? if checksums need to be crateds then until now: CRC/MD5 is possible with native tooling
## I know HPE left something (Maintenance..) out the last few years.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was used for basic file comparison, mostly the stat module now takes care of this

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so it can go away.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.

#def checksum(self, path, python_interp):
# # The following test is fish-compliant.
# #
# # In the following test, each condition is a check and logical
# # comparison (or or and) that sets the rc value. Every check is run so
# # the last check in the series to fail will be the rc that is
# # returned.
# #
# # If a check fails we error before invoking the hash functions because
# # hash functions may successfully take the hash of a directory on BSDs
# # (UFS filesystem?) which is not what the rest of the ansible code
# # expects
# #
# # If all of the available hashing methods fail we fail with an rc of
# # 0. This logic is added to the end of the cmd at the bottom of this
# # function.
#
# # Return codes:
# # checksum: success!
# # 0: Unknown error
# # 1: Remote file does not exist
# # 2: No read permissions on the file
# # 3: File is a directory
# # 4: No python interpreter
#
# # Quoting gets complex here. We're writing a python string that's
# # used by a variety of shells on the remote host to invoke a python
# # "one-liner".
# ##shell_escaped_path = shlex_quote(path)
# ##test = "set rc flag; [ -r %(p)s ] %(shell_or)s set rc 2; [ -f %(p)s ] %(shell_or)s set rc 1; [ -d %(p)s ] %(shell_and)s set rc 3; %(i)s -V 2>/dev/null %(shell_or)s set rc 4; [ x\"$rc\" != \"xflag\" ] %(shell_and)s echo \"$rc \"%(p)s %(shell_and)s exit 0" % dict(p=shell_escaped_path, i=python_interp, shell_and=self._SHELL_AND, shell_or=self._SHELL_OR) # NOQA
# ##csums = [
# ## u"({0} -c 'import hashlib; BLOCKSIZE = 65536; hasher = hashlib.sha1();{2}afile = open(\"'{1}'\", \"rb\"){2}buf = afile.read(BLOCKSIZE){2}while len(buf) > 0:{2}\thasher.update(buf){2}\tbuf = afile.read(BLOCKSIZE){2}afile.close(){2}print(hasher.hexdigest())' 2>/dev/null)".format(python_interp, shell_escaped_path, self._SHELL_EMBEDDED_PY_EOL), # NOQA Python > 2.4 (including python3)
# ## u"({0} -c 'import sha; BLOCKSIZE = 65536; hasher = sha.sha();{2}afile = open(\"'{1}'\", \"rb\"){2}buf = afile.read(BLOCKSIZE){2}while len(buf) > 0:{2}\thasher.update(buf){2}\tbuf = afile.read(BLOCKSIZE){2}afile.close(){2}print(hasher.hexdigest())' 2>/dev/null)".format(python_interp, shell_escaped_path, self._SHELL_EMBEDDED_PY_EOL), # NOQA Python == 2.4
# ##]
#
# ##cmd = (" %s " % self._SHELL_OR).join(csums)
# ##cmd = "%s; %s %s (echo \'0 \'%s)" % (test, cmd, self._SHELL_OR, shell_escaped_path)
# ##return cmd
# return ''