modules.machinery.xenserver

Package modules :: Package machinery :: Module xenserver |
[hide private]
[frames] | no frames]

# Source Code for Module modules.machinery.xenserver

      1  # Copyright (C) 2010-2013 Claudio Guarnieri.
      2  # Copyright (C) 2014-2016 Cuckoo Foundation.
      3  # This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
      4  # See the file 'docs/LICENSE' for copying permission.
      5
      6  """
      7  XenServer machinery.
      8  """
      9
     10  import threading
     11  import logging
     12
     13  from lib.cuckoo.common.abstracts import Machinery
     14  from lib.cuckoo.common.exceptions import CuckooMachineError
     15  from lib.cuckoo.common.exceptions import CuckooDependencyError
     16
     17  try:
     18      import XenAPI
     19      HAVE_XENAPI = True
     20  except ImportError:
     21      HAVE_XENAPI = False
     22
     23  log = logging.getLogger(__name__)
     24
     25 -class XenServerMachinery(Machinery):
     26      """Virtualization layer for XenServer using the XenAPI XML-RPC interface."""
     27
     28      LABEL = "uuid"
     29
     30      # Power States.
     31      RUNNING = "Running"
     32      PAUSED = "Paused"
     33      POWEROFF = "Halted"
     34      ABORTED = "Suspended"
     35
     36 -    def _initialize_check(self):
     37          """Check XenServer configuration, initialize a Xen API connection, and
     38          verify machine validity.
     39          """
     40          self._sessions = {}
     41
     42          if not HAVE_XENAPI:
     43              raise CuckooDependencyError("Unable to import XenAPI")
     44
     45          if not self.options.xenserver.user:
     46              raise CuckooMachineError("XenServer username missing, please add "
     47                                       "it to xenserver.conf.")
     48
     49          if not self.options.xenserver.password:
     50              raise CuckooMachineError("XenServer password missing, please add "
     51                                       "it to xenserver.conf")
     52
     53          if not self.options.xenserver.url:
     54              raise CuckooMachineError("XenServer url missing, please add it to "
     55                                       "xenserver.conf")
     56
     57          self._make_xenapi_session()
     58
     59          for machine in self.machines():
     60              uuid = machine.label
     61              (ref, vm) = self._check_vm(uuid)
     62
     63              if machine.snapshot:
     64                  self._check_snapshot(uuid, machine.snapshot)
     65              else:
     66                  self._check_disks_reset(vm)
     67
     68          super(XenServerMachinery, self)._initialize_check()
     69
     70      @property
     71 -    def session(self):
     72          tid = threading.current_thread().ident
     73          sess = self._sessions.get(tid, None)
     74          if sess is None:
     75              sess = self._make_xenapi_session(tid)
     76          return sess
     77
     78 -    def _make_xenapi_session(self, tid=None):
     79          tid = tid or threading.current_thread().ident
     80          try:
     81              sess = XenAPI.Session(self.options.xenserver.url)
     82          except:
     83              raise CuckooMachineError("Could not connect to XenServer: invalid "
     84                                       "or incorrect url, please ensure the url "
     85                                       "is correct in xenserver.conf")
     86
     87          try:
     88              sess.xenapi.login_with_password(
     89                  self.options.xenserver.user, self.options.xenserver.password
     90              )
     91          except:
     92              raise CuckooMachineError("Could not connect to XenServer: "
     93                                       "incorrect credentials, please ensure "
     94                                       "the user and password are correct in "
     95                                       "xenserver.conf")
     96          self._sessions[tid] = sess
     97          return sess
     98
     99 -    def _get_vm_ref(self, uuid):
    100          """Get a virtual machine reference.
    101          @param uuid: vm uuid
    102          """
    103          return self.session.xenapi.VM.get_by_uuid(uuid.lower())
    104
    105 -    def _get_vm_record(self, ref):
    106          """Get the virtual machine record.
    107          @param ref: vm reference
    108          """
    109          return self.session.xenapi.VM.get_record(ref)
    110
    111 -    def _get_vm_power_state(self, ref):
    112          """Get the virtual machine power state.
    113          @param ref: vm reference
    114          """
    115          return self.session.xenapi.VM.get_power_state(ref)
    116
    117 -    def _check_vm(self, uuid):
    118          """Check vm existence and validity.
    119          @param uuid: vm uuid
    120          """
    121          try:
    122              ref = self._get_vm_ref(uuid)
    123              vm = self._get_vm_record(ref)
    124          except XenAPI.Failure as e:
    125              raise CuckooMachineError("Vm not found: %s: %s"
    126                                       % (uuid, e.details[0]))
    127
    128          if vm["is_a_snapshot"]:
    129              raise CuckooMachineError("Vm is a snapshot: %s" % uuid)
    130
    131          if vm["is_a_template"]:
    132              raise CuckooMachineError("Vm is a template: %s" % uuid)
    133
    134          if vm["is_control_domain"]:
    135              raise CuckooMachineError("Vm is a control domain: %s" % uuid)
    136
    137          return (ref, vm)
    138
    139 -    def _check_snapshot(self, vm_uuid, snapshot_uuid):
    140          """Check snapshot existence and that the snapshot is of the specified
    141          vm uuid.
    142          @param vm_uuid: vm uuid
    143          @param snapshot_uuid: snapshot uuid
    144          """
    145          try:
    146              snapshot_ref = self._get_vm_ref(snapshot_uuid)
    147              snapshot = self._get_vm_record(snapshot_ref)
    148          except:
    149              raise CuckooMachineError("Snapshot not found: %s" % snapshot_uuid)
    150
    151          if not snapshot["is_a_snapshot"]:
    152              raise CuckooMachineError("Invalid snapshot: %s" % snapshot_uuid)
    153
    154          try:
    155              parent = self._get_vm_record(snapshot["snapshot_of"])
    156          except:
    157              raise CuckooMachineError("Invalid snapshot: %s" % snapshot_uuid)
    158
    159          parent_uuid = parent["uuid"]
    160          if parent_uuid != vm_uuid:
    161              raise CuckooMachineError("Snapshot does not belong to specified "
    162                                       "vm: %s" % snapshot_uuid)
    163
    164 -    def _check_disks_reset(self, vm):
    165          """Check whether each attached disk is set to reset on boot.
    166          @param vm: vm record
    167          """
    168          for ref in vm["VBDs"]:
    169              try:
    170                  vbd = self.session.xenapi.VBD.get_record(ref)
    171              except:
    172                  log.warning("Invalid VBD for vm %s: %s", vm["uuid"], ref)
    173                  continue
    174
    175              if vbd["type"] == "Disk":
    176                  vdi_ref = vbd["VDI"]
    177                  try:
    178                      vdi = self.session.xenapi.VDI.get_record(vdi_ref)
    179                  except:
    180                      log.warning("Invalid VDI for vm %s: %s", vm["uuid"],
    181                                  vdi_ref)
    182                      continue
    183
    184                  if vdi["on_boot"] != "reset" and vdi["read_only"] is False:
    185                      raise CuckooMachineError(
    186                          "Vm %s contains invalid VDI %s: disk is not reset on "
    187                          "boot. Please set the on-boot parameter to 'reset'."
    188                          % (vm["uuid"], vdi["uuid"]))
    189
    190 -    def _snapshot_from_vm_uuid(self, uuid):
    191          """Get the snapshot uuid from a virtual machine.
    192          @param uuid: vm uuid
    193          """
    194          machine = self.db.view_machine_by_label(uuid)
    195          return machine.snapshot
    196
    197 -    def _is_halted(self, vm):
    198          """Checks if the virtual machine is running.
    199          @param uuid: vm uuid
    200          """
    201          return vm["power_state"] == "Halted"
    202
    203 -    def start(self, label, task):
    204          """Start a virtual machine.
    205          @param label: vm uuid
    206          @param task: task object.
    207          """
    208          vm_ref = self._get_vm_ref(label)
    209          vm = self._get_vm_record(vm_ref)
    210
    211          if not self._is_halted(vm):
    212              raise CuckooMachineError("Vm is already running: %s", label)
    213
    214          snapshot = self._snapshot_from_vm_uuid(label)
    215          if snapshot:
    216              snapshot_ref = self._get_vm_ref(snapshot)
    217              try:
    218                  log.debug("Reverting vm %s to snapshot %s", label, snapshot)
    219                  self.session.xenapi.VM.revert(snapshot_ref)
    220                  log.debug("Revert completed for vm %s", label)
    221              except XenAPI.Failure as e:
    222                  raise CuckooMachineError("Unable to revert vm %s: %s"
    223                                           % (label, e.details[0]))
    224
    225              try:
    226                  log.debug("Resuming reverted vm %s", label)
    227                  self.session.xenapi.VM.resume(vm_ref, False, False)
    228              except XenAPI.Failure as e:
    229                  raise CuckooMachineError("Unable to resume vm %s: %s"
    230                                           % (label, e.details[0]))
    231          else:
    232              log.debug("No snapshot found for vm, booting: %s", label)
    233              try:
    234                  self.session.xenapi.VM.start(vm_ref, False, False)
    235              except XenAPI.Failure as e:
    236                  raise CuckooMachineError("Unable to start vm %s: %s"
    237                                           % (label, e.details[0]))
    238
    239          log.debug("Started vm: %s", label)
    240
    241 -    def stop(self, label=None):
    242          """Stop a virtual machine.
    243          @param label: vm uuid
    244          """
    245          ref = self._get_vm_ref(label)
    246          vm = self._get_vm_record(ref)
    247          if self._is_halted(vm):
    248              log.warning("Trying to stop an already stopped machine: %s", label)
    249          else:
    250              try:
    251                  self.session.xenapi.VM.hard_shutdown(ref)
    252              except XenAPI.Failure as e:
    253                  raise CuckooMachineError("Error shutting down virtual machine:"
    254                                           " %s: %s" % (label, e.details[0]))
    255
    256 -    def _list(self):
    257          """List available virtual machines.
    258          @raise CuckooMachineError: if unable to list virtual machines.
    259          """
    260          try:
    261              vm_list = []
    262              for ref in self.session.xenapi.VM.get_all():
    263                  vm = self._get_vm_record(ref)
    264                  vm_list.append(vm['uuid'])
    265          except:
    266              raise CuckooMachineError("Cannot list domains")
    267          else:
    268              return vm_list
    269
    270 -    def _status(self, label):
    271          """Gets current status of a vm.
    272          @param label: virtual machine uuid
    273          @return: status string.
    274          """
    275          ref = self._get_vm_ref(label)
    276          state = self._get_vm_power_state(ref)
    277          return state
    278