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: ...@@ -14,6 +14,7 @@ New features:
* New command: "slapos configure client" [Marco Mariani] * 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 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 getSoftwareReleaseListFromSoftwareProduct() SLAP method. [Cedric de Saint Martin]
* Add support for Software Product in request, supply and console. [Cedric de Saint Martin]
Major Improvements: Major Improvements:
......
...@@ -29,45 +29,31 @@ following informations : ...@@ -29,45 +29,31 @@ following informations :
See slapos.cfg.example for examples. 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. 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. request software installation.
For more information about those methods, please read the SLAP library For more information about those methods, please read the SLAP library
documentation. 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 request("mykvm", "http://www.url.com/path/to/current/best/known/kvm/software.cfg")
client slapos.cfg configuration file.
Also, each Software Release defined in this configuration file is translated can be simplified into ::
into a global variable to ease the request of those Sofware Releases.
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") * "slap" is an instance of the SLAP library. It is only used for advanced usages.
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" instance is obtained by doing :: "slap" instance is obtained by doing ::
slap = slapos.slap.slap() slap = slapos.slap.slap()
...@@ -81,22 +67,22 @@ Examples ...@@ -81,22 +67,22 @@ Examples
:: ::
>>> # Request instance >>> # Request instance
>>> request(kvm, "myuniquekvm") >>> request(product.kvm, "myuniquekvm")
>>> # Request instance on specific computer >>> # Request instance on specific computer
>>> request(kvm, "myotheruniquekvm", >>> request(product.kvm, "myotheruniquekvm",
filter_kw={ "computer_guid": "COMP-12345" }) filter_kw={ "computer_guid": "COMP-12345" })
>>> # Request instance, specifying parameters (here nbd_ip and nbd_port) >>> # 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", partition_parameter_kw={"nbd_ip":"2a01:e35:2e27:460:e2cb:4eff:fed9:48dc",
"nbd_port":"1024"}) "nbd_port":"1024"})
>>> # Request software installation on owned computer >>> # Request software installation on owned computer
>>> supply(kvm, "mycomputer") >>> supply(product.kvm, "mycomputer")
>>> # Fetch existing instance status >>> # Fetch existing instance status
>>> request(kvm, "myuniquekvm").getState() >>> request(product.kvm, "myuniquekvm").getState()
>>> # Fetch instance information on already launched instance >>> # 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): ...@@ -57,7 +57,7 @@ class ConsoleCommand(ClientConfigCommand):
def take_action(self, args): def take_action(self, args):
configp = self.fetch_config(args) configp = self.fetch_config(args)
conf = ClientConfig(args, configp) conf = ClientConfig(args, configp)
local = init(conf) local = init(conf, self.app.log)
if not any([args.python, args.ipython, args.bpython]): if not any([args.python, args.ipython, args.bpython]):
args.ipython = True args.ipython = True
......
...@@ -3,10 +3,11 @@ ...@@ -3,10 +3,11 @@
import pprint import pprint
from slapos.cli.config import ClientConfigCommand from slapos.cli.config import ClientConfigCommand
from slapos.client import init, ClientConfig from slapos.client import init, ClientConfig, _getSoftwareReleaseFromSoftwareString
from slapos.slap import ResourceNotReady from slapos.slap import ResourceNotReady
def parse_option_dict(options): def parse_option_dict(options):
""" """
Parse a list of option strings like foo=bar baz=qux and return a dictionary. Parse a list of option strings like foo=bar baz=qux and return a dictionary.
...@@ -63,12 +64,17 @@ class RequestCommand(ClientConfigCommand): ...@@ -63,12 +64,17 @@ class RequestCommand(ClientConfigCommand):
configp = self.fetch_config(args) configp = self.fetch_config(args)
conf = ClientConfig(args, configp) conf = ClientConfig(args, configp)
local = init(conf) local = init(conf, self.app.log)
do_request(self.app.log, conf, local) do_request(self.app.log, conf, local)
def do_request(logger, 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: if conf.software_url in local:
conf.software_url = local[conf.software_url] conf.software_url = local[conf.software_url]
try: try:
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from slapos.cli.config import ClientConfigCommand from slapos.cli.config import ClientConfigCommand
from slapos.client import init, ClientConfig from slapos.client import init, ClientConfig, _getSoftwareReleaseFromSoftwareString
class SupplyCommand(ClientConfigCommand): class SupplyCommand(ClientConfigCommand):
""" """
...@@ -23,23 +22,23 @@ class SupplyCommand(ClientConfigCommand): ...@@ -23,23 +22,23 @@ class SupplyCommand(ClientConfigCommand):
def take_action(self, args): def take_action(self, args):
configp = self.fetch_config(args) configp = self.fetch_config(args)
conf = ClientConfig(args, configp) conf = ClientConfig(args, configp)
local = init(conf) local = init(conf, self.app.log)
do_supply(self.app.log, args.software_url, args.node, local) 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 Request installation of Software Release
'software_url' on computer 'computer_id'. 'software_release' on computer 'computer_id'.
""" """
# XXX-Cedric Implement software_group support logger.info('Requesting software installation of %s...',
# XXX-Cedric Implement computer_group support software_release)
logger.info('Requesting installation of %s Software Release...', software_url)
software_release = _getSoftwareReleaseFromSoftwareString(
if software_url in local: logger, software_release, local['product'])
software_url = local[software_url]
local['slap'].registerSupply().supply( local['supply'](
software_release=software_url, software_release=software_release,
computer_guid=computer_id, computer_guid=computer_id,
state='available' state='available'
) )
......
...@@ -30,9 +30,12 @@ ...@@ -30,9 +30,12 @@
import atexit import atexit
import ConfigParser import ConfigParser
import os import os
import sys
import slapos.slap.slap import slapos.slap.slap
from slapos.slap import SoftwareProductCollection
SOFTWARE_PRODUCT_NAMESPACE = "product."
class ClientConfig(object): class ClientConfig(object):
state = None state = None
...@@ -79,7 +82,7 @@ class ClientConfig(object): ...@@ -79,7 +82,7 @@ class ClientConfig(object):
self.cert_file = os.path.expanduser(self.cert_file) self.cert_file = os.path.expanduser(self.cert_file)
def init(conf): def init(conf, logger):
"""Initialize Slap instance, connect to server and create """Initialize Slap instance, connect to server and create
aliases to common software releases""" aliases to common software releases"""
# XXX check certificate and key existence # XXX check certificate and key existence
...@@ -88,32 +91,33 @@ def init(conf): ...@@ -88,32 +91,33 @@ def init(conf):
key_file=conf.key_file, cert_file=conf.cert_file) key_file=conf.key_file, cert_file=conf.cert_file)
local = globals().copy() local = globals().copy()
local['slap'] = slap 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 # Create global shortcut functions to request instance and software
def shorthandRequest(*args, **kwargs): def shorthandRequest(*args, **kwargs):
return slap.registerOpenOrder().request(*args, **kwargs) return slap.registerOpenOrder().request(*args, **kwargs)
def shorthandSupply(*args, **kwargs): def shorthandSupply(*args, **kwargs):
# XXX-Cedric Implement computer_group support
return slap.registerSupply().supply(*args, **kwargs) return slap.registerSupply().supply(*args, **kwargs)
local['request'] = shorthandRequest local['request'] = shorthandRequest
local['supply'] = shorthandSupply local['supply'] = shorthandSupply
local['product'] = SoftwareProductCollection(logger, slap)
return local 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): def do_console(local):
# try to enable readline with completion and history # try to enable readline with completion and history
......
...@@ -154,6 +154,22 @@ class ISoftwareRelease(IBuildoutController): ...@@ -154,6 +154,22 @@ class ISoftwareRelease(IBuildoutController):
been correctly destroyed. 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): class IComputerPartition(IBuildoutController, IRequester):
""" """
Computer Partition interface specification Computer Partition interface specification
...@@ -474,7 +490,7 @@ class slap(Interface): ...@@ -474,7 +490,7 @@ class slap(Interface):
Instanciate a supply in the slap library. 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 Get the list of Software Releases from a product or from another related
Sofware Release, from a Software Product point of view. Sofware Release, from a Software Product point of view.
......
...@@ -31,6 +31,7 @@ Simple, easy to (un)marshall classes for slap client/server communication ...@@ -31,6 +31,7 @@ Simple, easy to (un)marshall classes for slap client/server communication
""" """
__all__ = ["slap", "ComputerPartition", "Computer", "SoftwareRelease", __all__ = ["slap", "ComputerPartition", "Computer", "SoftwareRelease",
"SoftwareProductCollection",
"Supply", "OpenOrder", "NotFoundError", "Unauthorized", "Supply", "OpenOrder", "NotFoundError", "Unauthorized",
"ResourceNotReady", "ServerError"] "ResourceNotReady", "ServerError"]
...@@ -180,6 +181,27 @@ class SoftwareRelease(SlapDocument): ...@@ -180,6 +181,27 @@ class SoftwareRelease(SlapDocument):
return getattr(self, '_requested_state', 'available') 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? # XXX What is this SoftwareInstance class?
class SoftwareInstance(SlapDocument): class SoftwareInstance(SlapDocument):
""" """
...@@ -740,9 +762,16 @@ class slap: ...@@ -740,9 +762,16 @@ class slap:
software_product_reference=None, software_release_url=None): software_product_reference=None, software_release_url=None):
url = '/getSoftwareReleaseListFromSoftwareProduct?' url = '/getSoftwareReleaseListFromSoftwareProduct?'
if software_product_reference: 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 url += 'software_product_reference=%s' % software_product_reference
else: 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 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