text: #!/usr/bin/python
  
  # LogicMonitor Ansible module for managing Collectors, Hosts and Hostgroups
  # Copyright (C) 2015  LogicMonitor
  #
  # This program is free software; you can redistribute it and/or modify
  # it under the terms of the GNU General Public License as published by
  # the Free Software Foundation; either version 3 of the License, or
  # (at your option) any later version.
  #
  # This program 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 General Public License for more details.
  #
  # You should have received a copy of the GNU General Public License
  # along with this program; if not, write to the Free Software Foundation,
  # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
  
  RETURN = '''
  ---
  success:
      description: flag indicating that execution was successful
      returned: success
      type: boolean
      sample: True
  ...
  '''
  
  
  ANSIBLE_METADATA = {'metadata_version': '1.0',
                      'status': ['preview'],
                      'supported_by': 'community'}
  
  
  DOCUMENTATION = '''
  ---
  module: logicmonitor
  short_description: Manage your LogicMonitor account through Ansible Playbooks
  description:
    - LogicMonitor is a hosted, full-stack, infrastructure monitoring platform.
    - This module manages hosts, host groups, and collectors within your LogicMonitor account.
  version_added: "2.2"
  author: [Ethan Culler-Mayeno (@ethanculler), Jeff Wozniak (@woz5999)]
  notes:
    - You must have an existing LogicMonitor account for this module to function.
  requirements: ["An existing LogicMonitor account", "Linux"]
  options:
    target:
      description:
        - The type of LogicMonitor object you wish to manage.
        - "Collector: Perform actions on a LogicMonitor collector."
        - NOTE You should use Ansible service modules such as M(service) or M(supervisorctl) for managing the Collector 'logicmonitor-agent' and 'logicmonitor-watchdog' services. Specifically, you'll probably want to start these services after a Collector add and stop these services before a Collector remove.
        - "Host: Perform actions on a host device."
        - "Hostgroup: Perform actions on a LogicMonitor host group."
        - 'NOTE Host and Hostgroup tasks should always be performed via delegate_to: localhost. There are no benefits to running these tasks on the remote host and doing so will typically cause problems.'
      required: true
      default: null
      choices: ['collector', 'host', 'datsource', 'hostgroup']
    action:
      description:
        - The action you wish to perform on target.
        - "Add: Add an object to your LogicMonitor account."
        - "Remove: Remove an object from your LogicMonitor account."
        - "Update: Update properties, description, or groups (target=host) for an object in your LogicMonitor account."
        - "SDT: Schedule downtime for an object in your LogicMonitor account."
      required: true
      default: null
      choices: ['add', 'remove', 'update', 'sdt']
    company:
      description:
        - The LogicMonitor account company name. If you would log in to your account at "superheroes.logicmonitor.com" you would use "superheroes."
      required: true
      default: null
    user:
      description:
        - A LogicMonitor user name. The module will authenticate and perform actions on behalf of this user.
      required: true
      default: null
    password:
      description:
          - The password of the specified LogicMonitor user
      required: true
      default: null
    collector:
      description:
        - The fully qualified domain name of a collector in your LogicMonitor account.
        - This is required for the creation of a LogicMonitor host (target=host action=add).
        - This is required for updating, removing or scheduling downtime for hosts if 'displayname' isn't specified (target=host action=update action=remove action=sdt).
      required: false
      default: null
    hostname:
      description:
        - The hostname of a host in your LogicMonitor account, or the desired hostname of a device to manage.
        - Optional for managing hosts (target=host).
      required: false
      default: 'hostname -f'
    displayname:
      description:
        - The display name of a host in your LogicMonitor account or the desired display name of a device to manage.
        - Optional for managing hosts (target=host).
      required: false
      default: 'hostname -f'
    description:
      description:
        - The long text description of the object in your LogicMonitor account.
        - Optional for managing hosts and host groups (target=host or target=hostgroup; action=add or action=update).
      required: false
      default: ""
    properties:
      description:
        - A dictionary of properties to set on the LogicMonitor host or host group.
        - Optional for managing hosts and host groups (target=host or target=hostgroup; action=add or action=update).
        - This parameter will add or update existing properties in your LogicMonitor account.
      required: false
      default: {}
    groups:
      description:
          - A list of groups that the host should be a member of.
          - Optional for managing hosts (target=host; action=add or action=update).
      required: false
      default: []
    id:
      description:
        - ID of the datasource to target.
        - Required for management of LogicMonitor datasources (target=datasource).
      required: false
      default: null
    fullpath:
      description:
        - The fullpath of the host group object you would like to manage.
        - Recommend running on a single Ansible host.
        - Required for management of LogicMonitor host groups (target=hostgroup).
      required: false
      default: null
    alertenable:
      description:
        - A boolean flag to turn alerting on or off for an object.
        - Optional for managing all hosts (action=add or action=update).
      required: false
      default: true
      choices: [true, false]
    starttime:
      description:
        - The time that the Scheduled Down Time (SDT) should begin.
        - Optional for managing SDT (action=sdt).
        - Y-m-d H:M
      required: false
      default: Now
    duration:
      description:
        - The duration (minutes) of the Scheduled Down Time (SDT).
        - Optional for putting an object into SDT (action=sdt).
      required: false
      default: 30
  ...
  '''
  EXAMPLES = '''
  # example of adding a new LogicMonitor collector to these devices
  ---
  - hosts: collectors
    remote_user: '{{ username }}'
    vars:
      company: mycompany
      user: myusername
      password: mypassword
    tasks:
    - name: Deploy/verify LogicMonitor collectors
      become: yes
      logicmonitor:
        target: collector
        action: add
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
  
  #example of adding a list of hosts into monitoring
  ---
  - hosts: hosts
    remote_user: '{{ username }}'
    vars:
      company: mycompany
      user: myusername
      password: mypassword
    tasks:
    - name: Deploy LogicMonitor Host
      # All tasks except for target=collector should use delegate_to: localhost
      logicmonitor:
        target: host
        action: add
        collector: mycompany-Collector
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
        groups: /servers/production,/datacenter1
        properties:
          snmp.community: secret
          dc: 1
          type: prod
      delegate_to: localhost
  
  #example of putting a datasource in SDT
  ---
  - hosts: localhost
    remote_user: '{{ username }}'
    vars:
      company: mycompany
      user: myusername
      password: mypassword
    tasks:
    - name: SDT a datasource
      # All tasks except for target=collector should use delegate_to: localhost
      logicmonitor:
        target: datasource
        action: sdt
        id: 123
        duration: 3000
        starttime: '2017-03-04 05:06'
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
  
  #example of creating a hostgroup
  ---
  - hosts: localhost
    remote_user: '{{ username }}'
    vars:
      company: mycompany
      user: myusername
      password: mypassword
    tasks:
    - name: Create a host group
      # All tasks except for target=collector should use delegate_to: localhost
      logicmonitor:
        target: hostgroup
        action: add
        fullpath: /servers/development
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
        properties:
          snmp.community: commstring
          type: dev
  
  #example of putting a list of hosts into SDT
  ---
  - hosts: hosts
    remote_user: '{{ username }}'
    vars:
      company: mycompany
      user: myusername
      password: mypassword
    tasks:
    - name: SDT hosts
      # All tasks except for target=collector should use delegate_to: localhost
      logicmonitor:
        target: host
        action: sdt
        duration: 3000
        starttime: '2016-11-10 09:08'
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
        collector: mycompany-Collector
      delegate_to: localhost
  
  #example of putting a host group in SDT
  ---
  - hosts: localhost
    remote_user: '{{ username }}'
    vars:
      company: mycompany
      user: myusername
      password: mypassword
    tasks:
    - name: SDT a host group
      # All tasks except for target=collector should use delegate_to: localhost
      logicmonitor:
        target: hostgroup
        action: sdt
        fullpath: /servers/development
        duration: 3000
        starttime: '2017-03-04 05:06'
        company=: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
  
  #example of updating a list of hosts
  ---
  - hosts: hosts
    remote_user: '{{ username }}'
    vars:
      company: mycompany
      user: myusername
      password: mypassword
    tasks:
    - name: Update a list of hosts
      # All tasks except for target=collector should use delegate_to: localhost
      logicmonitor:
        target: host
        action: update
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
        collector: mycompany-Collector
        groups: /servers/production,/datacenter5
        properties:
          snmp.community: commstring
          dc: 5
      delegate_to: localhost
  
  #example of updating a hostgroup
  ---
  - hosts: hosts
    remote_user: '{{ username }}'
    vars:
      company: mycompany
      user: myusername
      password: mypassword
    tasks:
    - name: Update a host group
      # All tasks except for target=collector should use delegate_to: localhost
      logicmonitor:
        target: hostgroup
        action: update
        fullpath: /servers/development
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
        properties:
          snmp.community: hg
          type: dev
          status: test
      delegate_to: localhost
  
  #example of removing a list of hosts from monitoring
  ---
  - hosts: hosts
    remote_user: '{{ username }}'
    vars:
      company: mycompany
      user: myusername
      password: mypassword
    tasks:
    - name: Remove LogicMonitor hosts
      # All tasks except for target=collector should use delegate_to: localhost
      logicmonitor:
        target: host
        action: remove
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
        collector: mycompany-Collector
      delegate_to: localhost
  
  #example of removing a host group
  ---
  - hosts: hosts
    remote_user: '{{ username }}'
    vars:
      company: mycompany
      user: myusername
      password: mypassword
    tasks:
    - name: Remove LogicMonitor development servers hostgroup
      # All tasks except for target=collector should use delegate_to: localhost
      logicmonitor:
        target: hostgroup
        action: remove
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
        fullpath: /servers/development
      delegate_to: localhost
    - name: Remove LogicMonitor servers hostgroup
      # All tasks except for target=collector should use delegate_to: localhost
      logicmonitor:
        target: hostgroup
        action: remove
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
        fullpath: /servers
      delegate_to: localhost
    - name: Remove LogicMonitor datacenter1 hostgroup
      # All tasks except for target=collector should use delegate_to: localhost
      logicmonitor:
        target: hostgroup
        action: remove
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
        fullpath: /datacenter1
      delegate_to: localhost
    - name: Remove LogicMonitor datacenter5 hostgroup
      # All tasks except for target=collector should use delegate_to: localhost
      logicmonitor:
        target: hostgroup
        action: remove
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
        fullpath: /datacenter5
      delegate_to: localhost
  
  ### example of removing a new LogicMonitor collector to these devices
  ---
  - hosts: collectors
    remote_user: '{{ username }}'
    vars:
      company: mycompany
      user: myusername
      password: mypassword
    tasks:
    - name: Remove LogicMonitor collectors
      become: yes
      logicmonitor:
        target: collector
        action: remove
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
  
  #complete example
  ---
  - hosts: localhost
    remote_user: '{{ username }}'
    vars:
      company: mycompany
      user: myusername
      password: mypassword
    tasks:
    - name: Create a host group
      logicmonitor:
        target: hostgroup
        action: add
        fullpath: /servers/production/database
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
        properties:
          snmp.community: commstring
    - name: SDT a host group
      logicmonitor:
        target: hostgroup
        action: sdt
        fullpath: /servers/production/web
        duration: 3000
        starttime: '2012-03-04 05:06'
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
  
  - hosts: collectors
    remote_user: '{{ username }}'
    vars:
      company: mycompany
      user: myusername
      password: mypassword
    tasks:
    - name: Deploy/verify LogicMonitor collectors
      logicmonitor:
        target: collector
        action: add
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
    - name: Place LogicMonitor collectors into 30 minute Scheduled downtime
      logicmonitor:
        target: collector
        action: sdt
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
    - name: Deploy LogicMonitor Host
      logicmonitor:
        target: host
        action: add
        collector: agent1.ethandev.com
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
        properties:
          snmp.community: commstring
          dc: 1
        groups: /servers/production/collectors, /datacenter1
      delegate_to: localhost
  
  - hosts: database-servers
    remote_user: '{{ username }}'
    vars:
      company: mycompany
      user: myusername
      password: mypassword
    tasks:
    - name: deploy logicmonitor hosts
      logicmonitor:
        target: host
        action: add
        collector: monitoring.dev.com
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
        properties:
          snmp.community: commstring
          type: db
          dc: 1
        groups: /servers/production/database, /datacenter1
      delegate_to: localhost
    - name: schedule 5 hour downtime for 2012-11-10 09:08
      logicmonitor:
        target: host
        action: sdt
        duration: 3000
        starttime: '2012-11-10 09:08'
        company: '{{ company }}'
        user: '{{ user }}'
        password: '{{ password }}'
      delegate_to: localhost
  '''
  
  import datetime
  import os
  import platform
  import socket
  import sys
  import types
  import urllib
  
  HAS_LIB_JSON = True
  try:
      import json
      # Detect the python-json library which is incompatible
      # Look for simplejson if that's the case
      try:
          if (
              not isinstance(json.loads, types.FunctionType) or
              not isinstance(json.dumps, types.FunctionType)
          ):
              raise ImportError
      except AttributeError:
          raise ImportError
  except ImportError:
      try:
          import simplejson as json
      except ImportError:
          HAS_LIB_JSON = False
      except SyntaxError:
          HAS_LIB_JSON = False
  
  
  
  class LogicMonitor(object):
  
      def __init__(self, module, **params):
          self.__version__ = "1.0-python"
          self.module = module
          self.module.debug("Instantiating LogicMonitor object")
  
          self.check_mode = False
          self.company = params["company"]
          self.user = params["user"]
          self.password = params["password"]
          self.fqdn = socket.getfqdn()
          self.lm_url = "logicmonitor.com/santaba"
          self.__version__ = self.__version__ + "-ansible-module"
  
      def rpc(self, action, params):
          """Make a call to the LogicMonitor RPC library
          and return the response"""
          self.module.debug("Running LogicMonitor.rpc")
  
          param_str = urllib.urlencode(params)
          creds = urllib.urlencode(
              {"c": self.company,
                  "u": self.user,
                  "p": self.password})
  
          if param_str:
              param_str = param_str + "&"
  
          param_str = param_str + creds
  
          try:
              url = ("https://" + self.company + "." + self.lm_url +
                     "/rpc/" + action + "?" + param_str)
  
              # Set custom LogicMonitor header with version
              headers = {"X-LM-User-Agent": self.__version__}
  
              # Set headers
              f = open_url(url, headers=headers)
  
              raw = f.read()
              resp = json.loads(raw)
              if resp["status"] == 403:
                  self.module.debug("Authentication failed.")
                  self.fail(msg="Error: " + resp["errmsg"])
              else:
                  return raw
          except IOError:
              ioe = get_exception()
              self.fail(msg="Error: Exception making RPC call to " +
                            "https://" + self.company + "." + self.lm_url +
                            "/rpc/" + action + "\nException" + str(ioe))
  
      def do(self, action, params):
          """Make a call to the LogicMonitor
           server \"do\" function"""
          self.module.debug("Running LogicMonitor.do...")
  
          param_str = urllib.urlencode(params)
          creds = (urllib.urlencode(
              {"c": self.company,
                  "u": self.user,
                  "p": self.password}))
  
          if param_str:
              param_str = param_str + "&"
          param_str = param_str + creds
  
          try:
              self.module.debug("Attempting to open URL: " +
                                "https://" + self.company + "." + self.lm_url +
                                "/do/" + action + "?" + param_str)
              f = open_url(
                  "https://" + self.company + "." + self.lm_url +
                  "/do/" + action + "?" + param_str)
              return f.read()
          except IOError:
              ioe = get_exception()
              self.fail(msg="Error: Exception making RPC call to " +
                            "https://" + self.company + "." + self.lm_url +
                            "/do/" + action + "\nException" + str(ioe))
  
      def get_collectors(self):
          """Returns a JSON object containing a list of
          LogicMonitor collectors"""
          self.module.debug("Running LogicMonitor.get_collectors...")
  
          self.module.debug("Making RPC call to 'getAgents'")
          resp = self.rpc("getAgents", {})
          resp_json = json.loads(resp)
  
          if resp_json["status"] is 200:
              self.module.debug("RPC call succeeded")
              return resp_json["data"]
          else:
              self.fail(msg=resp)
  
      def get_host_by_hostname(self, hostname, collector):
          """Returns a host object for the host matching the
          specified hostname"""
          self.module.debug("Running LogicMonitor.get_host_by_hostname...")
  
          self.module.debug("Looking for hostname " + hostname)
          self.module.debug("Making RPC call to 'getHosts'")
          hostlist_json = json.loads(self.rpc("getHosts", {"hostGroupId": 1}))
  
          if collector:
              if hostlist_json["status"] == 200:
                  self.module.debug("RPC call succeeded")
  
                  hosts = hostlist_json["data"]["hosts"]
  
                  self.module.debug(
                      "Looking for host matching: hostname " + hostname +
                      " and collector " + str(collector["id"]))
  
                  for host in hosts:
                      if (host["hostName"] == hostname and
                         host["agentId"] == collector["id"]):
  
                          self.module.debug("Host match found")
                          return host
                  self.module.debug("No host match found")
                  return None
              else:
                  self.module.debug("RPC call failed")
                  self.module.debug(hostlist_json)
          else:
              self.module.debug("No collector specified")
              return None
  
      def get_host_by_displayname(self, displayname):
          """Returns a host object for the host matching the
          specified display name"""
          self.module.debug("Running LogicMonitor.get_host_by_displayname...")
  
          self.module.debug("Looking for displayname " + displayname)
          self.module.debug("Making RPC call to 'getHost'")
          host_json = (json.loads(self.rpc("getHost",
                                  {"displayName": displayname})))
  
          if host_json["status"] == 200:
              self.module.debug("RPC call succeeded")
              return host_json["data"]
          else:
              self.module.debug("RPC call failed")
              self.module.debug(host_json)
              return None
  
      def get_collector_by_description(self, description):
          """Returns a JSON collector object for the collector
          matching the specified FQDN (description)"""
          self.module.debug(
              "Running LogicMonitor.get_collector_by_description..."
          )
  
          collector_list = self.get_collectors()
          if collector_list is not None:
              self.module.debug("Looking for collector with description {0}" +
                                description)
              for collector in collector_list:
                  if collector["description"] == description:
                      self.module.debug("Collector match found")
                      return collector
          self.module.debug("No collector match found")
          return None
  
      def get_group(self, fullpath):
          """Returns a JSON group object for the group matching the
          specified path"""
          self.module.debug("Running LogicMonitor.get_group...")
  
          self.module.debug("Making RPC call to getHostGroups")
          resp = json.loads(self.rpc("getHostGroups", {}))
  
          if resp["status"] == 200:
              self.module.debug("RPC called succeeded")
              groups = resp["data"]
  
              self.module.debug("Looking for group matching " + fullpath)
              for group in groups:
                  if group["fullPath"] == fullpath.lstrip('/'):
                      self.module.debug("Group match found")
                      return group
  
              self.module.debug("No group match found")
              return None
          else:
              self.module.debug("RPC call failed")
              self.module.debug(resp)
  
          return None
  
      def create_group(self, fullpath):
          """Recursively create a path of host groups.
          Returns the id of the newly created hostgroup"""
          self.module.debug("Running LogicMonitor.create_group...")
  
          res = self.get_group(fullpath)
          if res:
              self.module.debug("Group {0} exists." + fullpath)
              return res["id"]
  
          if fullpath == "/":
              self.module.debug("Specified group is root. Doing nothing.")
              return 1
          else:
              self.module.debug("Creating group named " + fullpath)
              self.module.debug("System changed")
              self.change = True
  
              if self.check_mode:
                  self.exit(changed=True)
  
              parentpath, name = fullpath.rsplit('/', 1)
              parentgroup = self.get_group(parentpath)
  
              parentid = 1
  
              if parentpath == "":
                  parentid = 1
              elif parentgroup:
                  parentid = parentgroup["id"]
              else:
                  parentid = self.create_group(parentpath)
  
              h = None
  
              # Determine if we're creating a group from host or hostgroup class
              if hasattr(self, '_build_host_group_hash'):
                  h = self._build_host_group_hash(
                      fullpath,
                      self.description,
                      self.properties,
                      self.alertenable)
                  h["name"] = name
                  h["parentId"] = parentid
              else:
                  h = {"name": name,
                       "parentId": parentid,
                       "alertEnable": True,
                       "description": ""}
  
              self.module.debug("Making RPC call to 'addHostGroup'")
              resp = json.loads(
                  self.rpc("addHostGroup", h))
  
              if resp["status"] == 200:
                  self.module.debug("RPC call succeeded")
                  return resp["data"]["id"]
              elif resp["errmsg"] == "The record already exists":
                  self.module.debug("The hostgroup already exists")
                  group = self.get_group(fullpath)
                  return group["id"]
              else:
                  self.module.debug("RPC call failed")
                  self.fail(
                      msg="Error: unable to create new hostgroup \"" +
                          name + "\".\n" + resp["errmsg"])
  
      def fail(self, msg):
          self.module.fail_json(msg=msg, changed=self.change, failed=True)
  
      def exit(self, changed):
          self.module.debug("Changed: " + changed)
          self.module.exit_json(changed=changed, success=True)
  
      def output_info(self, info):
          self.module.debug("Registering properties as Ansible facts")
          self.module.exit_json(changed=False, ansible_facts=info)
  
  
  class Collector(LogicMonitor):
  
      def __init__(self, params, module=None):
          """Initializor for the LogicMonitor Collector object"""
          self.change = False
          self.params = params
  
          LogicMonitor.__init__(self, module, **params)
          self.module.debug("Instantiating Collector object")
  
          if self.params['description']:
              self.description = self.params['description']
          else:
              self.description = self.fqdn
  
          self.info = self._get()
          self.installdir = "/usr/local/logicmonitor"
          self.platform = platform.system()
          self.is_64bits = sys.maxsize > 2**32
          self.duration = self.params['duration']
          self.starttime = self.params['starttime']
  
          if self.info is None:
              self.id = None
          else:
              self.id = self.info["id"]
  
      def create(self):
          """Idempotent function to make sure that there is
          a running collector installed and registered"""
          self.module.debug("Running Collector.create...")
  
          self._create()
          self.get_installer_binary()
          self.install()
  
      def remove(self):
          """Idempotent function to make sure that there is
          not a running collector installed and registered"""
          self.module.debug("Running Collector.destroy...")
  
          self._unreigster()
          self.uninstall()
  
      def get_installer_binary(self):
          """Download the LogicMonitor collector installer binary"""
          self.module.debug("Running Collector.get_installer_binary...")
  
          arch = 32
  
          if self.is_64bits:
              self.module.debug("64 bit system")
              arch = 64
          else:
              self.module.debug("32 bit system")
  
          if self.platform == "Linux" and self.id is not None:
              self.module.debug("Platform is Linux")
              self.module.debug("Agent ID is " + str(self.id))
  
              installfilepath = (self.installdir +
                                 "/logicmonitorsetup" +
                                 str(self.id) + "_" + str(arch) +
                                 ".bin")
  
              self.module.debug("Looking for existing installer at " +
                                installfilepath)
              if not os.path.isfile(installfilepath):
                  self.module.debug("No previous installer found")
                  self.module.debug("System changed")
                  self.change = True
  
                  if self.check_mode:
                      self.exit(changed=True)
  
                  self.module.debug("Downloading installer file")
                  # attempt to create the install dir before download
                  self.module.run_command("mkdir " + self.installdir)
  
                  try:
                      f = open(installfilepath, "w")
                      installer = (self.do("logicmonitorsetup",
                                           {"id": self.id,
                                            "arch": arch}))
                      f.write(installer)
                      f.closed
                  except:
                      self.fail(msg="Unable to open installer file for writing")
                      f.closed
              else:
                  self.module.debug("Collector installer already exists")
                  return installfilepath
  
          elif self.id is None:
              self.fail(
                  msg="Error: There is currently no collector " +
                      "associated with this device. To download " +
                      " the installer, first create a collector " +
                      "for this device.")
          elif self.platform != "Linux":
              self.fail(
                  msg="Error: LogicMonitor Collector must be " +
                  "installed on a Linux device.")
          else:
              self.fail(
                  msg="Error: Unable  to retrieve the installer from the server")
  
      def install(self):
          """Execute the LogicMonitor installer if not
          already installed"""
          self.module.debug("Running Collector.install...")
  
          if self.platform == "Linux":
              self.module.debug("Platform is Linux")
  
              installer = self.get_installer_binary()
  
              if self.info is None:
                  self.module.debug("Retriving collector information")
                  self.info = self._get()
  
              if not os.path.exists(self.installdir + "/agent"):
                  self.module.debug("System changed")
                  self.change = True
  
                  if self.check_mode:
                      self.exit(changed=True)
  
                  self.module.debug("Setting installer file permissions")
                  os.chmod(installer, 484)  # decimal for 0o744
  
                  self.module.debug("Executing installer")
                  ret_code, out, err = self.module.run_command(installer + " -y")
  
                  if ret_code != 0:
                      self.fail(msg="Error: Unable to install collector: " + err)
                  else:
                      self.module.debug("Collector installed successfully")
              else:
                  self.module.debug("Collector already installed")
          else:
              self.fail(
                  msg="Error: LogicMonitor Collector must be " +
                  "installed on a Linux device")
  
      def uninstall(self):
          """Uninstall LogicMontitor collector from the system"""
          self.module.debug("Running Collector.uninstall...")
  
          uninstallfile = self.installdir + "/agent/bin/uninstall.pl"
  
          if os.path.isfile(uninstallfile):
              self.module.debug("Collector uninstall file exists")
              self.module.debug("System changed")
              self.change = True
  
              if self.check_mode:
                  self.exit(changed=True)
  
              self.module.debug("Running collector uninstaller")
              ret_code, out, err = self.module.run_command(uninstallfile)
  
              if ret_code != 0:
                  self.fail(
                      msg="Error: Unable to uninstall collector: " + err)
              else:
                  self.module.debug("Collector successfully uninstalled")
          else:
              if os.path.exists(self.installdir + "/agent"):
                  (self.fail(
                      msg="Unable to uninstall LogicMonitor " +
                      "Collector. Can not find LogicMonitor " +
                      "uninstaller."))
  
      def sdt(self):
          """Create a scheduled down time
          (maintenance window) for this host"""
          self.module.debug("Running Collector.sdt...")
  
          self.module.debug("System changed")
          self.change = True
  
          if self.check_mode:
              self.exit(changed=True)
  
          duration = self.duration
          starttime = self.starttime
          offsetstart = starttime
  
          if starttime:
              self.module.debug("Start time specified")
              start = datetime.datetime.strptime(starttime, '%Y-%m-%d %H:%M')
              offsetstart = start
          else:
              self.module.debug("No start time specified. Using default.")
              start = datetime.datetime.utcnow()
  
              # Use user UTC offset
              self.module.debug("Making RPC call to 'getTimeZoneSetting'")
              accountresp = json.loads(self.rpc("getTimeZoneSetting", {}))
  
              if accountresp["status"] == 200:
                  self.module.debug("RPC call succeeded")
  
                  offset = accountresp["data"]["offset"]
                  offsetstart = start + datetime.timedelta(0, offset)
              else:
                  self.fail(msg="Error: Unable to retrieve timezone offset")
  
          offsetend = offsetstart + datetime.timedelta(0, int(duration)*60)
  
          h = {"agentId": self.id,
               "type": 1,
               "notifyCC": True,
               "year": offsetstart.year,
               "month": offsetstart.month-1,
               "day": offsetstart.day,
               "hour": offsetstart.hour,
               "minute": offsetstart.minute,
               "endYear": offsetend.year,
               "endMonth": offsetend.month-1,
               "endDay": offsetend.day,
               "endHour": offsetend.hour,
               "endMinute": offsetend.minute}
  
          self.module.debug("Making RPC call to 'setAgentSDT'")
          resp = json.loads(self.rpc("setAgentSDT", h))
  
          if resp["status"] == 200:
              self.module.debug("RPC call succeeded")
              return resp["data"]
          else:
              self.module.debug("RPC call failed")
              self.fail(msg=resp["errmsg"])
  
      def site_facts(self):
          """Output current properties information for the Collector"""
          self.module.debug("Running Collector.site_facts...")
  
          if self.info:
              self.module.debug("Collector exists")
              props = self.get_properties(True)
  
              self.output_info(props)
          else:
              self.fail(msg="Error: Collector doesn't exit.")
  
      def _get(self):
          """Returns a JSON object representing this collector"""
          self.module.debug("Running Collector._get...")
          collector_list = self.get_collectors()
  
          if collector_list is not None:
              self.module.debug("Collectors returned")
              for collector in collector_list:
                  if collector["description"] == self.description:
                      return collector
          else:
              self.module.debug("No collectors returned")
              return None
  
      def _create(self):
          """Create a new collector in the associated
          LogicMonitor account"""
          self.module.debug("Running Collector._create...")
  
          if self.platform == "Linux":
              self.module.debug("Platform is Linux")
              ret = self.info or self._get()
  
              if ret is None:
                  self.change = True
                  self.module.debug("System changed")
  
                  if self.check_mode:
                      self.exit(changed=True)
  
                  h = {"autogen": True,
                       "description": self.description}
  
                  self.module.debug("Making RPC call to 'addAgent'")
                  create = (json.loads(self.rpc("addAgent", h)))
  
                  if create["status"] is 200:
                      self.module.debug("RPC call succeeded")
                      self.info = create["data"]
                      self.id = create["data"]["id"]
                      return create["data"]
                  else:
                      self.fail(msg=create["errmsg"])
              else:
                  self.info = ret
                  self.id = ret["id"]
                  return ret
          else:
              self.fail(
                  msg="Error: LogicMonitor Collector must be " +
                  "installed on a Linux device.")
  
      def _unreigster(self):
          """Delete this collector from the associated
          LogicMonitor account"""
          self.module.debug("Running Collector._unreigster...")
  
          if self.info is None:
              self.module.debug("Retrieving collector information")
              self.info = self._get()
  
          if self.info is not None:
              self.module.debug("Collector found")
              self.module.debug("System changed")
              self.change = True
  
              if self.check_mode:
                  self.exit(changed=True)
  
              self.module.debug("Making RPC call to 'deleteAgent'")
              delete = json.loads(self.rpc("deleteAgent",
                                           {"id": self.id}))
  
              if delete["status"] is 200:
                  self.module.debug("RPC call succeeded")
                  return delete
              else:
                  # The collector couldn't unregister. Start the service again
                  self.module.debug("Error unregistering collecting. " +
                                    delete["errmsg"])
                  self.fail(msg=delete["errmsg"])
          else:
              self.module.debug("Collector not found")
              return None
  
  
  class Host(LogicMonitor):
  
      def __init__(self, params, module=None):
          """Initializor for the LogicMonitor host object"""
          self.change = False
          self.params = params
          self.collector = None
  
          LogicMonitor.__init__(self, module, **self.params)
          self.module.debug("Instantiating Host object")
  
          if self.params["hostname"]:
              self.module.debug("Hostname is " + self.params["hostname"])
              self.hostname = self.params['hostname']
          else:
              self.module.debug("No hostname specified. Using " + self.fqdn)
              self.hostname = self.fqdn
  
          if self.params["displayname"]:
              self.module.debug("Display name is " + self.params["displayname"])
              self.displayname = self.params['displayname']
          else:
              self.module.debug("No display name specified. Using " + self.fqdn)
              self.displayname = self.fqdn
  
          # Attempt to host information via display name of host name
          self.module.debug("Attempting to find host by displayname " +
                            self.displayname)
          info = self.get_host_by_displayname(self.displayname)
  
          if info is not None:
              self.module.debug("Host found by displayname")
              # Used the host information to grab the collector description
              # if not provided
              if (not hasattr(self.params, "collector") and
                 "agentDescription" in info):
                  self.module.debug("Setting collector from host response. " +
                                    "Collector " + info["agentDescription"])
                  self.params["collector"] = info["agentDescription"]
          else:
              self.module.debug("Host not found by displayname")
  
          # At this point, a valid collector description is required for success
          # Check that the description exists or fail
          if self.params["collector"]:
              self.module.debug(
                  "Collector specified is " +
                  self.params["collector"]
              )
              self.collector = (self.get_collector_by_description(
                                self.params["collector"]))
          else:
              self.fail(msg="No collector specified.")
  
          # If the host wasn't found via displayname, attempt by hostname
          if info is None:
              self.module.debug("Attempting to find host by hostname " +
                                self.hostname)
              info = self.get_host_by_hostname(self.hostname, self.collector)
  
          self.info = info
          self.properties = self.params["properties"]
          self.description = self.params["description"]
          self.starttime = self.params["starttime"]
          self.duration = self.params["duration"]
          self.alertenable = self.params["alertenable"]
          if self.params["groups"] is not None:
              self.groups = self._strip_groups(self.params["groups"])
          else:
              self.groups = None
  
      def create(self):
          """Idemopotent function to create if missing,
          update if changed, or skip"""
          self.module.debug("Running Host.create...")
  
          self.update()
  
      def get_properties(self):
          """Returns a hash of the properties
          associated with this LogicMonitor host"""
          self.module.debug("Running Host.get_properties...")
  
          if self.info:
              self.module.debug("Making RPC call to 'getHostProperties'")
              properties_json = (json.loads(self.rpc("getHostProperties",
                                            {'hostId': self.info["id"],
                                             "filterSystemProperties": True})))
  
              if properties_json["status"] == 200:
                  self.module.debug("RPC call succeeded")
                  return properties_json["data"]
              else:
                  self.module.debug("Error: there was an issue retrieving the " +
                                    "host properties")
                  self.module.debug(properties_json["errmsg"])
  
                  self.fail(msg=properties_json["status"])
          else:
              self.module.debug(
                  "Unable to find LogicMonitor host which matches " +
                  self.displayname + " (" + self.hostname + ")"
              )
              return None
  
      def set_properties(self, propertyhash):
          """update the host to have the properties
          contained in the property hash"""
          self.module.debug("Running Host.set_properties...")
          self.module.debug("System changed")
          self.change = True
  
          if self.check_mode:
              self.exit(changed=True)
  
          self.module.debug("Assigning property hash to host object")
          self.properties = propertyhash
  
      def add(self):
          """Add this device to monitoring
          in your LogicMonitor account"""
          self.module.debug("Running Host.add...")
  
          if self.collector and not self.info:
              self.module.debug("Host not registered. Registering.")
              self.module.debug("System changed")
              self.change = True
  
              if self.check_mode:
                  self.exit(changed=True)
  
              h = self._build_host_hash(
                  self.hostname,
                  self.displayname,
                  self.collector,
                  self.description,
                  self.groups,
                  self.properties,
                  self.alertenable)
  
              self.module.debug("Making RPC call to 'addHost'")
              resp = json.loads(self.rpc("addHost", h))
  
              if resp["status"] == 200:
                  self.module.debug("RPC call succeeded")
                  return resp["data"]
              else:
                  self.module.debug("RPC call failed")
                  self.module.debug(resp)
                  return resp["errmsg"]
          elif self.collector is None:
              self.fail(msg="Specified collector doesn't exist")
          else:
              self.module.debug("Host already registered")
  
      def update(self):
          """This method takes changes made to this host
          and applies them to the corresponding host
          in your LogicMonitor account."""
          self.module.debug("Running Host.update...")
  
          if self.info:
              self.module.debug("Host already registed")
              if self.is_changed():
                  self.module.debug("System changed")
                  self.change = True
  
                  if self.check_mode:
                      self.exit(changed=True)
  
                  h = (self._build_host_hash(
                       self.hostname,
                       self.displayname,
                       self.collector,
                       self.description,
                       self.groups,
                       self.properties,
                       self.alertenable))
                  h["id"] = self.info["id"]
                  h["opType"] = "replace"
  
                  self.module.debug("Making RPC call to 'updateHost'")
                  resp = json.loads(self.rpc("updateHost", h))
  
                  if resp["status"] == 200:
                      self.module.debug("RPC call succeeded")
                  else:
                      self.module.debug("RPC call failed")
                      self.fail(msg="Error: unable to update the host.")
              else:
                  self.module.debug(
                      "Host properties match supplied properties. " +
                      "No changes to make."
                  )
                  return self.info
          else:
              self.module.debug("Host not registed. Registering")
              self.module.debug("System changed")
              self.change = True
  
              if self.check_mode:
                  self.exit(changed=True)
  
              return self.add()
  
      def remove(self):
          """Remove this host from your LogicMonitor account"""
          self.module.debug("Running Host.remove...")
  
          if self.info:
              self.module.debug("Host registered")
              self.module.debug("System changed")
              self.change = True
  
              if self.check_mode:
                  self.exit(changed=True)
  
              self.module.debug("Making RPC call to 'deleteHost'")
              resp = json.loads(self.rpc("deleteHost",
                                         {"hostId": self.info["id"],
                                          "deleteFromSystem": True,
                                          "hostGroupId": 1}))
  
              if resp["status"] == 200:
                  self.module.debug(resp)
                  self.module.debug("RPC call succeeded")
                  return resp
              else:
                  self.module.debug("RPC call failed")
                  self.module.debug(resp)
                  self.fail(msg=resp["errmsg"])
  
          else:
              self.module.debug("Host not registered")
  
      def is_changed(self):
          """Return true if the host doesn't
          match the LogicMonitor account"""
          self.module.debug("Running Host.is_changed")
  
          ignore = ['system.categories', 'snmp.version']
  
          hostresp = self.get_host_by_displayname(self.displayname)
  
          if hostresp is None:
              hostresp = self.get_host_by_hostname(self.hostname, self.collector)
  
          if hostresp:
              self.module.debug("Comparing simple host properties")
              if hostresp["alertEnable"] != self.alertenable:
                  return True
  
              if hostresp["description"] != self.description:
                  return True
  
              if hostresp["displayedAs"] != self.displayname:
                  return True
  
              if (self.collector and
                 hasattr(self.collector, "id") and
                 hostresp["agentId"] != self.collector["id"]):
                  return True
  
              self.module.debug("Comparing groups.")
              if self._compare_groups(hostresp) is True:
                  return True
  
              propresp = self.get_properties()
  
              if propresp:
                  self.module.debug("Comparing properties.")
                  if self._compare_props(propresp, ignore) is True:
                      return True
              else:
                  self.fail(
                      msg="Error: Unknown error retrieving host properties")
  
              return False
          else:
              self.fail(msg="Error: Unknown error retrieving host information")
  
      def sdt(self):
          """Create a scheduled down time
          (maintenance window) for this host"""
          self.module.debug("Running Host.sdt...")
          if self.info:
              self.module.debug("System changed")
              self.change = True
  
              if self.check_mode:
                  self.exit(changed=True)
  
              duration = self.duration
              starttime = self.starttime
              offset = starttime
  
              if starttime:
                  self.module.debug("Start time specified")
                  start = datetime.datetime.strptime(starttime, '%Y-%m-%d %H:%M')
                  offsetstart = start
              else:
                  self.module.debug("No start time specified. Using default.")
                  start = datetime.datetime.utcnow()
  
                  # Use user UTC offset
                  self.module.debug("Making RPC call to 'getTimeZoneSetting'")
                  accountresp = (json.loads(self.rpc("getTimeZoneSetting", {})))
  
                  if accountresp["status"] == 200:
                      self.module.debug("RPC call succeeded")
  
                      offset = accountresp["data"]["offset"]
                      offsetstart = start + datetime.timedelta(0, offset)
                  else:
                      self.fail(
                          msg="Error: Unable to retrieve timezone offset")
  
              offsetend = offsetstart + datetime.timedelta(0, int(duration)*60)
  
              h = {"hostId": self.info["id"],
                   "type": 1,
                   "year": offsetstart.year,
                   "month": offsetstart.month - 1,
                   "day": offsetstart.day,
                   "hour": offsetstart.hour,
                   "minute": offsetstart.minute,
                   "endYear": offsetend.year,
                   "endMonth": offsetend.month - 1,
                   "endDay": offsetend.day,
                   "endHour": offsetend.hour,
                   "endMinute": offsetend.minute}
  
              self.module.debug("Making RPC call to 'setHostSDT'")
              resp = (json.loads(self.rpc("setHostSDT", h)))
  
              if resp["status"] == 200:
                  self.module.debug("RPC call succeeded")
                  return resp["data"]
              else:
                  self.module.debug("RPC call failed")
                  self.fail(msg=resp["errmsg"])
          else:
              self.fail(msg="Error: Host doesn't exit.")
  
      def site_facts(self):
          """Output current properties information for the Host"""
          self.module.debug("Running Host.site_facts...")
  
          if self.info:
              self.module.debug("Host exists")
              props = self.get_properties()
  
              self.output_info(props)
          else:
              self.fail(msg="Error: Host doesn't exit.")
  
      def _build_host_hash(self,
                           hostname,
                           displayname,
                           collector,
                           description,
                           groups,
                           properties,
                           alertenable):
          """Return a property formatted hash for the
          creation of a host using the rpc function"""
          self.module.debug("Running Host._build_host_hash...")
  
          h = {}
          h["hostName"] = hostname
          h["displayedAs"] = displayname
          h["alertEnable"] = alertenable
  
          if collector:
              self.module.debug("Collector property exists")
              h["agentId"] = collector["id"]
          else:
              self.fail(
                  msg="Error: No collector found. Unable to build host hash.")
  
          if description:
              h["description"] = description
  
          if groups is not None and groups is not []:
              self.module.debug("Group property exists")
              groupids = ""
  
              for group in groups:
                  groupids = groupids + str(self.create_group(group)) + ","
  
              h["hostGroupIds"] = groupids.rstrip(',')
  
          if properties is not None and properties is not {}:
              self.module.debug("Properties hash exists")
              propnum = 0
              for key, value in properties.items():
                  h["propName" + str(propnum)] = key
                  h["propValue" + str(propnum)] = value
                  propnum = propnum + 1
  
          return h
  
      def _verify_property(self, propname):
          """Check with LogicMonitor server to
          verify property is unchanged"""
          self.module.debug("Running Host._verify_property...")
  
          if self.info:
              self.module.debug("Host is registered")
              if propname not in self.properties:
                  self.module.debug("Property " + propname + " does not exist")
                  return False
              else:
                  self.module.debug("Property " + propname + " exists")
                  h = {"hostId": self.info["id"],
                       "propName0": propname,
                       "propValue0": self.properties[propname]}
  
                  self.module.debug("Making RCP call to 'verifyProperties'")
                  resp = json.loads(self.rpc('verifyProperties', h))
  
                  if resp["status"] == 200:
                      self.module.debug("RPC call succeeded")
                      return resp["data"]["match"]
                  else:
                      self.fail(
                          msg="Error: unable to get verification " +
                              "from server.\n%s" % resp["errmsg"])
          else:
              self.fail(
                  msg="Error: Host doesn't exist. Unable to verify properties")
  
      def _compare_groups(self, hostresp):
          """Function to compare the host's current
          groups against provided groups"""
          self.module.debug("Running Host._compare_groups")
  
          g = []
          fullpathinids = hostresp["fullPathInIds"]
          self.module.debug("Building list of groups")
          for path in fullpathinids:
              if path != []:
                  h = {'hostGroupId': path[-1]}
  
                  hgresp = json.loads(self.rpc("getHostGroup", h))
  
                  if (hgresp["status"] == 200 and
                     hgresp["data"]["appliesTo"] == ""):
  
                      g.append(path[-1])
  
          if self.groups is not None:
              self.module.debug("Comparing group lists")
              for group in self.groups:
                  groupjson = self.get_group(group)
  
                  if groupjson is None:
                      self.module.debug("Group mismatch. No result.")
                      return True
                  elif groupjson['id'] not in g:
                      self.module.debug("Group mismatch. ID doesn't exist.")
                      return True
                  else:
                      g.remove(groupjson['id'])
  
              if g != []:
                  self.module.debug("Group mismatch. New ID exists.")
                  return True
              self.module.debug("Groups match")
  
      def _compare_props(self, propresp, ignore):
          """Function to compare the host's current
          properties against provided properties"""
          self.module.debug("Running Host._compare_props...")
          p = {}
  
          self.module.debug("Creating list of properties")
          for prop in propresp:
              if prop["name"] not in ignore:
                  if ("*******" in prop["value"] and
                     self._verify_property(prop["name"])):
                      p[prop["name"]] = self.properties[prop["name"]]
                  else:
                      p[prop["name"]] = prop["value"]
  
          self.module.debug("Comparing properties")
          # Iterate provided properties and compare to received properties
          for prop in self.properties:
              if (prop not in p or
                 p[prop] != self.properties[prop]):
                  self.module.debug("Properties mismatch")
                  return True
          self.module.debug("Properties match")
  
      def _strip_groups(self, groups):
          """Function to strip whitespace from group list.
          This function provides the user some flexibility when
          formatting group arguments """
          self.module.debug("Running Host._strip_groups...")
          return map(lambda x: x.strip(), groups)
  
  
  class Datasource(LogicMonitor):
  
      def __init__(self, params, module=None):
          """Initializor for the LogicMonitor Datasource object"""
          self.change = False
          self.params = params
  
          LogicMonitor.__init__(self, module, **params)
          self.module.debug("Instantiating Datasource object")
  
          self.id = self.params["id"]
          self.starttime = self.params["starttime"]
          self.duration = self.params["duration"]
  
      def sdt(self):
          """Create a scheduled down time
          (maintenance window) for this host"""
          self.module.debug("Running Datasource.sdt...")
  
          self.module.debug("System changed")
          self.change = True
  
          if self.check_mode:
              self.exit(changed=True)
  
          duration = self.duration
          starttime = self.starttime
          offsetstart = starttime
  
          if starttime:
              self.module.debug("Start time specified")
              start = datetime.datetime.strptime(starttime, '%Y-%m-%d %H:%M')
              offsetstart = start
          else:
              self.module.debug("No start time specified. Using default.")
              start = datetime.datetime.utcnow()
  
              # Use user UTC offset
              self.module.debug("Making RPC call to 'getTimeZoneSetting'")
              accountresp = json.loads(self.rpc("getTimeZoneSetting", {}))
  
              if accountresp["status"] == 200:
                  self.module.debug("RPC call succeeded")
  
                  offset = accountresp["data"]["offset"]
                  offsetstart = start + datetime.timedelta(0, offset)
              else:
                  self.fail(msg="Error: Unable to retrieve timezone offset")
  
          offsetend = offsetstart + datetime.timedelta(0, int(duration)*60)
  
          h = {"hostDataSourceId": self.id,
               "type": 1,
               "notifyCC": True,
               "year": offsetstart.year,
               "month": offsetstart.month-1,
               "day": offsetstart.day,
               "hour": offsetstart.hour,
               "minute": offsetstart.minute,
               "endYear": offsetend.year,
               "endMonth": offsetend.month-1,
               "endDay": offsetend.day,
               "endHour": offsetend.hour,
               "endMinute": offsetend.minute}
  
          self.module.debug("Making RPC call to 'setHostDataSourceSDT'")
          resp = json.loads(self.rpc("setHostDataSourceSDT", h))
  
          if resp["status"] == 200:
              self.module.debug("RPC call succeeded")
              return resp["data"]
          else:
              self.module.debug("RPC call failed")
              self.fail(msg=resp["errmsg"])
  
  
  class Hostgroup(LogicMonitor):
  
      def __init__(self, params, module=None):
          """Initializor for the LogicMonitor host object"""
          self.change = False
          self.params = params
  
          LogicMonitor.__init__(self, module, **self.params)
          self.module.debug("Instantiating Hostgroup object")
  
          self.fullpath = self.params["fullpath"]
          self.info = self.get_group(self.fullpath)
          self.properties = self.params["properties"]
          self.description = self.params["description"]
          self.starttime = self.params["starttime"]
          self.duration = self.params["duration"]
          self.alertenable = self.params["alertenable"]
  
      def create(self):
          """Wrapper for self.update()"""
          self.module.debug("Running Hostgroup.create...")
          self.update()
  
      def get_properties(self, final=False):
          """Returns a hash of the properties
          associated with this LogicMonitor host"""
          self.module.debug("Running Hostgroup.get_properties...")
  
          if self.info:
              self.module.debug("Group found")
  
              self.module.debug("Making RPC call to 'getHostGroupProperties'")
              properties_json = json.loads(self.rpc(
                  "getHostGroupProperties",
                  {'hostGroupId': self.info["id"],
                   "finalResult": final}))
  
              if properties_json["status"] == 200:
                  self.module.debug("RPC call succeeded")
                  return properties_json["data"]
              else:
                  self.module.debug("RPC call failed")
                  self.fail(msg=properties_json["status"])
          else:
              self.module.debug("Group not found")
              return None
  
      def set_properties(self, propertyhash):
          """Update the host to have the properties
          contained in the property hash"""
          self.module.debug("Running Hostgroup.set_properties")
  
          self.module.debug("System changed")
          self.change = True
  
          if self.check_mode:
              self.exit(changed=True)
  
          self.module.debug("Assigning property has to host object")
          self.properties = propertyhash
  
      def add(self):
          """Idempotent function to ensure that the host
          group exists in your LogicMonitor account"""
          self.module.debug("Running Hostgroup.add")
  
          if self.info is None:
              self.module.debug("Group doesn't exist. Creating.")
              self.module.debug("System changed")
              self.change = True
  
              if self.check_mode:
                  self.exit(changed=True)
  
              self.create_group(self.fullpath)
              self.info = self.get_group(self.fullpath)
  
              self.module.debug("Group created")
              return self.info
          else:
              self.module.debug("Group already exists")
  
      def update(self):
          """Idempotent function to ensure the host group settings
          (alertenable, properties, etc) in the
          LogicMonitor account match the current object."""
          self.module.debug("Running Hostgroup.update")
  
          if self.info:
              if self.is_changed():
                  self.module.debug("System changed")
                  self.change = True
  
                  if self.check_mode:
                      self.exit(changed=True)
  
                  h = self._build_host_group_hash(
                      self.fullpath,
                      self.description,
                      self.properties,
                      self.alertenable)
                  h["opType"] = "replace"
  
                  if self.fullpath != "/":
                      h["id"] = self.info["id"]
  
                  self.module.debug("Making RPC call to 'updateHostGroup'")
                  resp = json.loads(self.rpc("updateHostGroup", h))
  
                  if resp["status"] == 200:
                      self.module.debug("RPC call succeeded")
                      return resp["data"]
                  else:
                      self.module.debug("RPC call failed")
                      self.fail(msg="Error: Unable to update the " +
                                "host.\n" + resp["errmsg"])
              else:
                  self.module.debug(
                      "Group properties match supplied properties. " +
                      "No changes to make"
                  )
                  return self.info
          else:
              self.module.debug("Group doesn't exist. Creating.")
  
              self.module.debug("System changed")
              self.change = True
  
              if self.check_mode:
                  self.exit(changed=True)
  
              return self.add()
  
      def remove(self):
          """Idempotent function to ensure the host group
          does not exist in your LogicMonitor account"""
          self.module.debug("Running Hostgroup.remove...")
  
          if self.info:
              self.module.debug("Group exists")
              self.module.debug("System changed")
              self.change = True
  
              if self.check_mode:
                  self.exit(changed=True)
  
              self.module.debug("Making RPC call to 'deleteHostGroup'")
              resp = json.loads(self.rpc("deleteHostGroup",
                                         {"hgId": self.info["id"]}))
  
              if resp["status"] == 200:
                  self.module.debug(resp)
                  self.module.debug("RPC call succeeded")
                  return resp
              elif resp["errmsg"] == "No such group":
                  self.module.debug("Group doesn't exist")
              else:
                  self.module.debug("RPC call failed")
                  self.module.debug(resp)
                  self.fail(msg=resp["errmsg"])
          else:
              self.module.debug("Group doesn't exist")
  
      def is_changed(self):
          """Return true if the host doesn't match
          the LogicMonitor account"""
          self.module.debug("Running Hostgroup.is_changed...")
  
          ignore = []
          group = self.get_group(self.fullpath)
          properties = self.get_properties()
  
          if properties is not None and group is not None:
              self.module.debug("Comparing simple group properties")
              if (group["alertEnable"] != self.alertenable or
                 group["description"] != self.description):
  
                  return True
  
              p = {}
  
              self.module.debug("Creating list of properties")
              for prop in properties:
                  if prop["name"] not in ignore:
                      if ("*******" in prop["value"] and
                         self._verify_property(prop["name"])):
  
                          p[prop["name"]] = (
                              self.properties[prop["name"]])
                      else:
                          p[prop["name"]] = prop["value"]
  
              self.module.debug("Comparing properties")
              if set(p) != set(self.properties):
                  return True
          else:
              self.module.debug("No property information received")
              return False
  
      def sdt(self, duration=30, starttime=None):
          """Create a scheduled down time
          (maintenance window) for this host"""
          self.module.debug("Running Hostgroup.sdt")
  
          self.module.debug("System changed")
          self.change = True
  
          if self.check_mode:
              self.exit(changed=True)
  
          duration = self.duration
          starttime = self.starttime
          offset = starttime
  
          if starttime:
              self.module.debug("Start time specified")
              start = datetime.datetime.strptime(starttime, '%Y-%m-%d %H:%M')
              offsetstart = start
          else:
              self.module.debug("No start time specified. Using default.")
              start = datetime.datetime.utcnow()
  
              # Use user UTC offset
              self.module.debug("Making RPC call to 'getTimeZoneSetting'")
              accountresp = json.loads(self.rpc("getTimeZoneSetting", {}))
  
              if accountresp["status"] == 200:
                  self.module.debug("RPC call succeeded")
  
                  offset = accountresp["data"]["offset"]
                  offsetstart = start + datetime.timedelta(0, offset)
              else:
                  self.fail(
                      msg="Error: Unable to retrieve timezone offset")
  
          offsetend = offsetstart + datetime.timedelta(0, int(duration)*60)
  
          h = {"hostGroupId": self.info["id"],
               "type": 1,
               "year": offsetstart.year,
               "month": offsetstart.month-1,
               "day": offsetstart.day,
               "hour": offsetstart.hour,
               "minute": offsetstart.minute,
               "endYear": offsetend.year,
               "endMonth": offsetend.month-1,
               "endDay": offsetend.day,
               "endHour": offsetend.hour,
               "endMinute": offsetend.minute}
  
          self.module.debug("Making RPC call to setHostGroupSDT")
          resp = json.loads(self.rpc("setHostGroupSDT", h))
  
          if resp["status"] == 200:
              self.module.debug("RPC call succeeded")
              return resp["data"]
          else:
              self.module.debug("RPC call failed")
              self.fail(msg=resp["errmsg"])
  
      def site_facts(self):
          """Output current properties information for the Hostgroup"""
          self.module.debug("Running Hostgroup.site_facts...")
  
          if self.info:
              self.module.debug("Group exists")
              props = self.get_properties(True)
  
              self.output_info(props)
          else:
              self.fail(msg="Error: Group doesn't exit.")
  
      def _build_host_group_hash(self,
                                 fullpath,
                                 description,
                                 properties,
                                 alertenable):
          """Return a property formatted hash for the
          creation of a hostgroup using the rpc function"""
          self.module.debug("Running Hostgroup._build_host_hash")
  
          h = {}
          h["alertEnable"] = alertenable
  
          if fullpath == "/":
              self.module.debug("Group is root")
              h["id"] = 1
          else:
              self.module.debug("Determining group path")
              parentpath, name = fullpath.rsplit('/', 1)
              parent = self.get_group(parentpath)
  
              h["name"] = name
  
              if parent:
                  self.module.debug("Parent group " +
                                    str(parent["id"]) + " found.")
                  h["parentID"] = parent["id"]
              else:
                  self.module.debug("No parent group found. Using root.")
                  h["parentID"] = 1
  
          if description:
              self.module.debug("Description property exists")
              h["description"] = description
  
          if properties != {}:
              self.module.debug("Properties hash exists")
              propnum = 0
              for key, value in properties.items():
                  h["propName" + str(propnum)] = key
                  h["propValue" + str(propnum)] = value
                  propnum = propnum + 1
  
          return h
  
      def _verify_property(self, propname):
          """Check with LogicMonitor server
          to verify property is unchanged"""
          self.module.debug("Running Hostgroup._verify_property")
  
          if self.info:
              self.module.debug("Group exists")
              if propname not in self.properties:
                  self.module.debug("Property " + propname + " does not exist")
                  return False
              else:
                  self.module.debug("Property " + propname + " exists")
                  h = {"hostGroupId": self.info["id"],
                       "propName0": propname,
                       "propValue0": self.properties[propname]}
  
                  self.module.debug("Making RCP call to 'verifyProperties'")
                  resp = json.loads(self.rpc('verifyProperties', h))
  
                  if resp["status"] == 200:
                      self.module.debug("RPC call succeeded")
                      return resp["data"]["match"]
                  else:
                      self.fail(
                          msg="Error: unable to get verification " +
                              "from server.\n%s" % resp["errmsg"])
          else:
              self.fail(
                  msg="Error: Group doesn't exist. Unable to verify properties")
  
  
  def selector(module):
      """Figure out which object and which actions
      to take given the right parameters"""
  
      if module.params["target"] == "collector":
          target = Collector(module.params, module)
      elif module.params["target"] == "host":
          # Make sure required parameter collector is specified
          if ((module.params["action"] == "add" or
              module.params["displayname"] is None) and
             module.params["collector"] is None):
              module.fail_json(
                  msg="Parameter 'collector' required.")
  
          target = Host(module.params, module)
      elif module.params["target"] == "datasource":
          # Validate target specific required parameters
          if module.params["id"] is not None:
              # make sure a supported action was specified
              if module.params["action"] == "sdt":
                  target = Datasource(module.params, module)
              else:
                  errmsg = ("Error: Unexpected action \"" +
                            module.params["action"] + "\" was specified.")
                  module.fail_json(msg=errmsg)
  
      elif module.params["target"] == "hostgroup":
          # Validate target specific required parameters
          if module.params["fullpath"] is not None:
              target = Hostgroup(module.params, module)
          else:
              module.fail_json(
                  msg="Parameter 'fullpath' required for target 'hostgroup'")
      else:
          module.fail_json(
              msg="Error: Unexpected target \"" + module.params["target"] +
                  "\" was specified.")
  
      if module.params["action"].lower() == "add":
          action = target.create
      elif module.params["action"].lower() == "remove":
          action = target.remove
      elif module.params["action"].lower() == "sdt":
          action = target.sdt
      elif module.params["action"].lower() == "update":
          action = target.update
      else:
          errmsg = ("Error: Unexpected action \"" + module.params["action"] +
                    "\" was specified.")
          module.fail_json(msg=errmsg)
  
      action()
      module.exit_json(changed=target.change)
  
  
  def main():
      TARGETS = [
          "collector",
          "host",
          "datasource",
          "hostgroup"]
  
      ACTIONS = [
          "add",
          "remove",
          "sdt",
          "update"]
  
      module = AnsibleModule(
          argument_spec=dict(
              target=dict(required=True, default=None, choices=TARGETS),
              action=dict(required=True, default=None, choices=ACTIONS),
              company=dict(required=True, default=None),
              user=dict(required=True, default=None),
              password=dict(required=True, default=None, no_log=True),
  
              collector=dict(required=False, default=None),
              hostname=dict(required=False, default=None),
              displayname=dict(required=False, default=None),
              id=dict(required=False, default=None),
              description=dict(required=False, default=""),
              fullpath=dict(required=False, default=None),
              starttime=dict(required=False, default=None),
              duration=dict(required=False, default=30),
              properties=dict(required=False, default={}, type="dict"),
              groups=dict(required=False, default=[], type="list"),
              alertenable=dict(required=False, default="true", choices=BOOLEANS)
          ),
          supports_check_mode=True
      )
  
      if HAS_LIB_JSON is not True:
          module.fail_json(msg="Unable to load JSON library")
  
      selector(module)
  
  
  from ansible.module_utils.basic import *
  from ansible.module_utils.urls import *
  from ansible.module_utils.urls import open_url
  
  
  if __name__ == "__main__":
      main()
  