request.py 6.87 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
##############################################################################
#
# Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
##############################################################################
import logging

from slapos import slap as slapmodule
30
import slapos.recipe.librecipe.generic as librecipe
31

32
DEFAULT_SOFTWARE_TYPE = 'RootSoftwareInstance'
33

34
class Recipe(object):
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
  """
  Request a partition to a slap master.
  Can provide parameters to that partition and fetches its connection
  parameters.

  Input:
    server-url
    key-file (optional)
    cert-file (optional)
      Used to contact slap master.

    computer-id
    partition-id
      Current partition's identifiers.
      Must match key's credentials if given.

    name (optional, defaults to section name)
      Name (reference) of requested partition.

    software-url
      URL of a software definition to request an instance of.

57 58 59 60
    software-type
      Software type of requested instance, among those provided by the
      definition from software-url.

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
    slave (optional, defaults to false)
      Set to "true" when requesting a slave instance, ie just setting a set of
      parameters in an existing instance.

    sla (optional)
      Whitespace-separated list of Service Level Agreement names.
      Each name must correspond to a "sla-<name>" option.
      Used to specify what a suitable partition would be.
      Possible names depend on master's capabilities.

    config (optional)
      Whitespace-separated list of partition parameter names.
      Each name must correspond to a "config-<name>" option.
      Possible names depend on requested partition's software type.

    return (optional)
      Whitespace-separated list of expected partition-published value names.
      Options will be created from them, in the form of "connection-<name>"
      As long as requested partition doesn't publish all those values,
      installation of request section will fail.
      Possible names depend on requested partition's software type.

    Output:
      See "return" input key.
  """
86
  failed = None
87 88

  def __init__(self, buildout, name, options):
89
    self.logger = logging.getLogger(name)
90 91 92

    slap = slapmodule.slap()

93
    software_url = options['software-url']
94
    name = options['name']
95

96 97 98
    slap.initializeConnection(options['server-url'],
                              options.get('key-file'),
                              options.get('cert-file'),
99
                             )
100 101
    request = slap.registerComputerPartition(
      options['computer-id'], options['partition-id']).request
102

103
    return_parameters = []
104
    if 'return' in options:
105
      return_parameters = [str(parameter).strip()
106
                          for parameter in options['return'].split()]
107
    else:
108
      self.logger.debug("No parameter to return to main instance."
109
        "Be careful about that...")
110

111
    software_type = options.get('software-type', DEFAULT_SOFTWARE_TYPE)
112 113 114

    filter_kw = {}
    if 'sla' in options:
115 116
      for sla_parameter in options['sla'].split():
        filter_kw[sla_parameter] = options['sla-%s' % sla_parameter]
117 118 119

    partition_parameter_kw = {}
    if 'config' in options:
120 121 122
      for config_parameter in options['config'].split():
        partition_parameter_kw[config_parameter] = \
            options['config-%s' % config_parameter]
123 124 125

    isSlave = options.get('slave', '').lower() in \
        librecipe.GenericBaseRecipe.TRUE_VALUES
126
    self.instance = request(software_url, software_type,
127
      name, partition_parameter_kw=partition_parameter_kw,
128
      filter_kw=filter_kw, shared=isSlave)
129

130 131 132 133 134 135 136
    self._raise_resource_not_ready = None
    try:
        # XXX what is the right way to get a global id?
        options['instance_guid'] = self.instance.getId()
    except slapmodule.ResourceNotReady as exc:
        self._raise_resource_not_ready = exc

137
    for param in return_parameters:
138
      try:
139
        options['connection-%s' % param] = str(
140
          self.instance.getConnectionParameter(param))
141
      except (slapmodule.NotFoundError, slapmodule.ServerError):
142 143 144
        options['connection-%s' % param] = ''
        if self.failed is None:
          self.failed = param
145 146

  def install(self):
147 148 149
    if self._raise_resource_not_ready:
      raise slapmodule.ResourceNotReady(self._resource_not_ready)

150
    if self.failed is not None:
151 152
      # Check instance status to know if instance has been deployed
      try:
153 154 155 156
        if self.instance.getComputerId() is not None:
          status = self.instance.getState()
        else:
          status = 'not ready yet'
157
      except (slapmodule.NotFoundError, slapmodule.ServerError):
158
        status = 'not ready yet'
159
      except AttributeError:
160
        status = 'unknown'
161
      error_message = 'Connection parameter %s not found. '\
162
          'Status of requested instance is: %s. If this error persists, '\
163
          'check status of this instance.' % (self.failed, status)
164 165
      self.logger.error(error_message)
      raise KeyError(error_message)
166 167 168
    return []

  update = install
169 170 171 172 173 174 175 176 177 178

class RequestOptional(Recipe):
  """
  Request a SlapOS instance. Won't fail if instance is not ready.
  Same as slapos.cookbook:request, but won't raise in case of problem.
  """
  def install(self):
    if self.failed is not None:
      # Check instance status to know if instance has been deployed
      try:
179 180 181 182
        if self.instance.getComputerId() is not None:
          status = self.instance.getState()
        else:
          status = 'not ready yet'
183 184 185 186 187 188 189
      except (slapmodule.NotFoundError, slapmodule.ServerError):
        status = 'not ready yet'
      except AttributeError:
        status = 'unknown'
      error_message = 'Connection parameter %s not found. '\
          'Requested instance is currently %s. If this error persists, '\
          'check status of this instance.' % (self.failed, status)
190
      self.logger.warning(error_message)
191
    return []
192 193

  update = install