Commit 62495c3c authored by Cédric de Saint Martin's avatar Cédric de Saint Martin Committed by Gabriel Monnerat

[SlapOS Core] Add support for Software Product in request, supply and console.

parent 55ee1128
......@@ -14,6 +14,7 @@ New features:
* New command: "slapos configure client" [Marco Mariani]
* add new "root_check" option in slapos configuration file (true by default) allowing to bypass "am I root" checks in slapos. [Cedric de Saint Martin]
* Add support for getSoftwareReleaseListFromSoftwareProduct() SLAP method. [Cedric de Saint Martin]
* Add support for Software Product in request, supply and console. [Cedric de Saint Martin]
Major Improvements:
......
......@@ -29,45 +29,31 @@ following informations :
See slapos.cfg.example for examples.
Global functions
~~~~~~~~~~~~~~~~
Global functions/variables
~~~~~~~~~~~~~~~~~~~~~~~~~~
* request() is a shorthand for slap.registerOpenOrder().request() allowing
* "request()" is a shorthand for slap.registerOpenOrder().request() allowing
to request instances.
* supply() is a shorthand for slap.registerSupply().supply() allowing to
* "supply()" is a shorthand for slap.registerSupply().supply() allowing to
request software installation.
For more information about those methods, please read the SLAP library
documentation.
* "product" is an instance of slap.SoftwareProductCollection whose only goal is to retrieve
the URL of the best Software Release of a given Software Product as attribute.
for each attribute call, it will retrieve from the SlapOS Master the best
available Software Release URL and return it.
Global aliases
~~~~~~~~~~~~~~
This allows to request instances in a few words, i.e::
"software_list" is a list containing all the Software Release URLs defined in
client slapos.cfg configuration file.
request("mykvm", "http://www.url.com/path/to/current/best/known/kvm/software.cfg")
Also, each Software Release defined in this configuration file is translated
into a global variable to ease the request of those Sofware Releases.
can be simplified into ::
This allows to request instances in a few words, i.e::
request("mykvm", product.kvm)
request("http://www.url.com/path/to/kvm/software.cfg", "mykvm")
can be simplified into ::
request(kvm, "mykvm")
If the slapos.cfg file contains ::
alias =
kvm http://www.url.com/path/to/kvm/software.cfg
Global objects
~~~~~~~~~~~~~~
"slap" is an instance of the SLAP library. It is only used for advanced usages.
* "slap" is an instance of the SLAP library. It is only used for advanced usages.
"slap" instance is obtained by doing ::
slap = slapos.slap.slap()
......@@ -81,22 +67,22 @@ Examples
::
>>> # Request instance
>>> request(kvm, "myuniquekvm")
>>> request(product.kvm, "myuniquekvm")
>>> # Request instance on specific computer
>>> request(kvm, "myotheruniquekvm",
>>> request(product.kvm, "myotheruniquekvm",
filter_kw={ "computer_guid": "COMP-12345" })
>>> # Request instance, specifying parameters (here nbd_ip and nbd_port)
>>> request(kvm, "mythirduniquekvm",
>>> request(product.kvm, "mythirduniquekvm",
partition_parameter_kw={"nbd_ip":"2a01:e35:2e27:460:e2cb:4eff:fed9:48dc",
"nbd_port":"1024"})
>>> # Request software installation on owned computer
>>> supply(kvm, "mycomputer")
>>> supply(product.kvm, "mycomputer")
>>> # Fetch existing instance status
>>> request(kvm, "myuniquekvm").getState()
>>> request(product.kvm, "myuniquekvm").getState()
>>> # Fetch instance information on already launched instance
>>> request(kvm, "myuniquekvm").getConnectionParameter("url")
>>> request(product.kvm, "myuniquekvm").getConnectionParameter("url")
......@@ -57,7 +57,7 @@ class ConsoleCommand(ClientConfigCommand):
def take_action(self, args):
configp = self.fetch_config(args)
conf = ClientConfig(args, configp)
local = init(conf)
local = init(conf, self.app.log)
if not any([args.python, args.ipython, args.bpython]):
args.ipython = True
......
......@@ -3,10 +3,11 @@
import pprint
from slapos.cli.config import ClientConfigCommand
from slapos.client import init, ClientConfig
from slapos.client import init, ClientConfig, _getSoftwareReleaseFromSoftwareString
from slapos.slap import ResourceNotReady
def parse_option_dict(options):
"""
Parse a list of option strings like foo=bar baz=qux and return a dictionary.
......@@ -63,12 +64,17 @@ class RequestCommand(ClientConfigCommand):
configp = self.fetch_config(args)
conf = ClientConfig(args, configp)
local = init(conf)
local = init(conf, self.app.log)
do_request(self.app.log, conf, local)
def do_request(logger, conf, local):
logger.info('Requesting %s...', conf.reference)
logger.info('Requesting %s as instance of %s...',
conf.reference, conf.software_url)
conf.software_url = _getSoftwareReleaseFromSoftwareString(
logger, conf.software_url, local['product'])
if conf.software_url in local:
conf.software_url = local[conf.software_url]
try:
......
# -*- coding: utf-8 -*-
from slapos.cli.config import ClientConfigCommand
from slapos.client import init, ClientConfig
from slapos.client import init, ClientConfig, _getSoftwareReleaseFromSoftwareString
class SupplyCommand(ClientConfigCommand):
"""
......@@ -23,23 +22,23 @@ class SupplyCommand(ClientConfigCommand):
def take_action(self, args):
configp = self.fetch_config(args)
conf = ClientConfig(args, configp)
local = init(conf)
local = init(conf, self.app.log)
do_supply(self.app.log, args.software_url, args.node, local)
def do_supply(logger, software_url, computer_id, local):
def do_supply(logger, software_release, computer_id, local):
"""
Request installation of Software Release
'software_url' on computer 'computer_id'.
'software_release' on computer 'computer_id'.
"""
# XXX-Cedric Implement software_group support
# XXX-Cedric Implement computer_group support
logger.info('Requesting installation of %s Software Release...', software_url)
if software_url in local:
software_url = local[software_url]
local['slap'].registerSupply().supply(
software_release=software_url,
logger.info('Requesting software installation of %s...',
software_release)
software_release = _getSoftwareReleaseFromSoftwareString(
logger, software_release, local['product'])
local['supply'](
software_release=software_release,
computer_guid=computer_id,
state='available'
)
......
......@@ -30,9 +30,12 @@
import atexit
import ConfigParser
import os
import sys
import slapos.slap.slap
from slapos.slap import SoftwareProductCollection
SOFTWARE_PRODUCT_NAMESPACE = "product."
class ClientConfig(object):
state = None
......@@ -79,7 +82,7 @@ class ClientConfig(object):
self.cert_file = os.path.expanduser(self.cert_file)
def init(conf):
def init(conf, logger):
"""Initialize Slap instance, connect to server and create
aliases to common software releases"""
# XXX check certificate and key existence
......@@ -88,32 +91,33 @@ def init(conf):
key_file=conf.key_file, cert_file=conf.cert_file)
local = globals().copy()
local['slap'] = slap
# Create aliases as global variables
try:
alias = conf.alias.split('\n')
except AttributeError:
alias = []
software_list = []
for software in alias:
if software:
name, url = software.split(' ')
software_list.append(name)
local[name] = url
# Create global variable too see available aliases
local['software_list'] = software_list
# Create global shortcut functions to request instance and software
def shorthandRequest(*args, **kwargs):
return slap.registerOpenOrder().request(*args, **kwargs)
def shorthandSupply(*args, **kwargs):
# XXX-Cedric Implement computer_group support
return slap.registerSupply().supply(*args, **kwargs)
local['request'] = shorthandRequest
local['supply'] = shorthandSupply
local['product'] = SoftwareProductCollection(logger, slap)
return local
def _getSoftwareReleaseFromSoftwareString(logger, software_string, product):
"""
If Software string is a product:
Return the best Software Release URL of the Software Product "X" of the
string "product.X".
Else, return as is.
"""
if not software_string.startswith(SOFTWARE_PRODUCT_NAMESPACE):
return software_string
try:
return product.get(software_string[len(SOFTWARE_PRODUCT_NAMESPACE):])
except AttributeError as e:
logger.error('Error: %s Exiting now.' % e.message)
sys.exit(1)
def do_console(local):
# try to enable readline with completion and history
......
......@@ -154,6 +154,22 @@ class ISoftwareRelease(IBuildoutController):
been correctly destroyed.
"""
class ISoftwareProductCollection(Interface):
"""
Fake object representing the abstract of all Software Products.
Can be used to call "Product().mysoftwareproduct", or, simpler,
"product.mysoftwareproduct", to get the best Software Release URL of the
Software Product "mysoftwareproduct".
Example: product.kvm will have the value of the latest Software
Release URL of KVM.
"""
def get(software_product):
"""
Return the best Software Release URL of the Software Product
software_product, by querying SlapOS Master.
"""
class IComputerPartition(IBuildoutController, IRequester):
"""
Computer Partition interface specification
......@@ -474,7 +490,7 @@ class slap(Interface):
Instanciate a supply in the slap library.
"""
def getSoftwareReleaseListFromProduct(software_product_reference, software_release_url):
def getSoftwareReleaseListFromSoftwareProduct(software_product_reference, software_release_url):
"""
Get the list of Software Releases from a product or from another related
Sofware Release, from a Software Product point of view.
......
......@@ -31,6 +31,7 @@ Simple, easy to (un)marshall classes for slap client/server communication
"""
__all__ = ["slap", "ComputerPartition", "Computer", "SoftwareRelease",
"SoftwareProductCollection",
"Supply", "OpenOrder", "NotFoundError", "Unauthorized",
"ResourceNotReady", "ServerError"]
......@@ -180,6 +181,27 @@ class SoftwareRelease(SlapDocument):
return getattr(self, '_requested_state', 'available')
class SoftwareProductCollection(object):
zope.interface.implements(interface.ISoftwareProductCollection)
def __init__(self, logger, slap):
self.logger = logger
self.slap = slap
self.__getattr__ = self.get
def get(self, software_product):
self.logger.info('Getting best Software Release corresponging to '
'this Software Product...')
software_release_list = \
self.slap.getSoftwareReleaseListFromSoftwareProduct(software_product)
try:
software_release_url = software_release_list[0] # First is best one.
self.logger.info('Found as %s.' % software_release_url)
return software_release_url
except IndexError:
raise AttributeError('No Software Release corresponding to this '
'Software Product has been found.')
# XXX What is this SoftwareInstance class?
class SoftwareInstance(SlapDocument):
"""
......@@ -740,9 +762,16 @@ class slap:
software_product_reference=None, software_release_url=None):
url = '/getSoftwareReleaseListFromSoftwareProduct?'
if software_product_reference:
assert(software_release_url is None)
if software_release_url is not None:
raise AttributeError('Both software_product_reference and '
'software_release_url parameters are specified.')
url += 'software_product_reference=%s' % software_product_reference
else:
assert(software_release_url is not None)
if software_release_url is None:
raise AttributeError('None of software_product_reference and '
'software_release_url parameters are specified.')
url += 'software_release_url=%s' % software_release_url
return xml_marshaller.loads(self._connection_helper.GET(url))
result = xml_marshaller.loads(self._connection_helper.GET(url))
assert(type(result) == list)
return result
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment