[libvirt] [PATCH v2 sandbox] virt-sandbox-image: switch to use URI to identify templates
Cedric Bosdonnat
cbosdonnat at suse.com
Tue Sep 22 08:40:02 UTC 2015
On Mon, 2015-09-21 at 15:45 +0100, Daniel P. Berrange wrote:
> Currently the CLI syntax is somewhat docker specific requiring
> inclusion of --registry arg to identify the docker download
> server. Other app containers have a notion of download server,
> but don't separate it from the template name.
>
> This patch removes that docker-ism by changing to use a URI
> for identifying the template image. So instead of
>
> virt-sandbox-image download \
> --source docker --registry index.docker.io
> --username dan --password 123456 ubuntu:15.04
>
> You can use
>
> virt-sandbox-image download docker://dan:123456@index.docker.io/ubuntu?tag=15.04
>
> The only mandatory part is the source prefix and image name, so
> that can shorten to just
>
> virt-sandbox-image download docker:///ubuntu
>
> to pull down the latest ubuntu image, from the default registry
> using no authentication.
> ---
>
> Changed in v2:
>
> - Rebase against master, instead of (unpushed) docker volume patch
>
> libvirt-sandbox/image/cli.py | 71 +++++--------
> libvirt-sandbox/image/sources/DockerSource.py | 142 ++++++++++++++------------
> libvirt-sandbox/image/sources/Source.py | 29 +++---
> libvirt-sandbox/image/template.py | 110 ++++++++++++++++++++
> 4 files changed, 228 insertions(+), 124 deletions(-)
> create mode 100644 libvirt-sandbox/image/template.py
>
> diff --git a/libvirt-sandbox/image/cli.py b/libvirt-sandbox/image/cli.py
> index 1718cc5..4d02289 100755
> --- a/libvirt-sandbox/image/cli.py
> +++ b/libvirt-sandbox/image/cli.py
> @@ -3,7 +3,7 @@
> # Authors: Daniel P. Berrange <berrange at redhat.com>
> # Eren Yagdiran <erenyagdiran at gmail.com>
> #
> -# Copyright (C) 2013 Red Hat, Inc.
> +# Copyright (C) 2013-2015 Red Hat, Inc.
> # Copyright (C) 2015 Universitat Politècnica de Catalunya.
> #
> # This program is free software; you can redistribute it and/or modify
> @@ -34,6 +34,8 @@ import subprocess
> import random
> import string
>
> +from libvirt_sandbox.image import template
> +
> if os.geteuid() == 0:
> default_template_dir = "/var/lib/libvirt/templates"
> default_image_dir = "/var/lib/libvirt/images"
> @@ -44,15 +46,6 @@ else:
> debug = False
> verbose = False
>
> -import importlib
> -def dynamic_source_loader(name):
> - name = name[0].upper() + name[1:]
> - modname = "libvirt_sandbox.image.sources." + name + "Source"
> - mod = importlib.import_module(modname)
> - classname = name + "Source"
> - classimpl = getattr(mod, classname)
> - return classimpl()
> -
> gettext.bindtextdomain("libvirt-sandbox", "/usr/share/locale")
> gettext.textdomain("libvirt-sandbox")
> try:
> @@ -73,11 +66,10 @@ def info(msg):
>
> def download(args):
> try:
> - dynamic_source_loader(args.source).download_template(templatename=args.template,
> - templatedir=args.template_dir,
> - registry=args.registry,
> - username=args.username,
> - password=args.password)
> + tmpl = template.Template.from_uri(args.template)
> + source = tmpl.get_source_impl()
> + source.download_template(template=tmpl,
> + templatedir=args.template_dir)
> except IOError,e:
> print "Source %s cannot be found in given path" %args.source
> except Exception,e:
> @@ -85,17 +77,21 @@ def download(args):
>
> def delete(args):
> try:
> - dynamic_source_loader(args.source).delete_template(templatename=args.template,
> - templatedir=args.template_dir)
> + tmpl = template.Template.from_uri(args.template)
> + source = tmpl.get_source_impl()
> + source.delete_template(template=tmpl,
> + templatedir=args.template_dir)
> except Exception,e:
> print "Delete Error %s", str(e)
>
> def create(args):
> try:
> - dynamic_source_loader(args.source).create_template(templatename=args.template,
> - templatedir=args.template_dir,
> - connect=args.connect,
> - format=args.format)
> + tmpl = template.Template.from_uri(args.template)
> + source = tmpl.get_source_impl()
> + source.create_template(template=tmpl,
> + templatedir=args.template_dir,
> + connect=args.connect,
> + format=args.format)
> except Exception,e:
> print "Create Error %s" % str(e)
>
> @@ -103,19 +99,22 @@ def run(args):
> try:
> if args.connect is not None:
> check_connect(args.connect)
> - source = dynamic_source_loader(args.source)
> +
> + tmpl = template.Template.from_uri(args.template)
> + source = tmpl.get_source_impl()
> +
> name = args.name
> if name is None:
> randomid = ''.join(random.choice(string.lowercase) for i in range(10))
> - name = args.template + ":" + randomid
> + name = tmpl.path[1:] + ":" + randomid
>
> - diskfile = source.get_disk(templatename=args.template,
> + diskfile = source.get_disk(template=tmpl,
> templatedir=args.template_dir,
> imagedir=args.image_dir,
> sandboxname=name)
>
> format = "qcow2"
> - commandToRun = source.get_command(args.template, args.template_dir, args.args)
> + commandToRun = source.get_command(tmpl, args.template_dir, args.args)
> if len(commandToRun) == 0:
> commandToRun = ["/bin/sh"]
> cmd = ['virt-sandbox', '--name', name]
> @@ -129,7 +128,7 @@ def run(args):
> params.append('-N')
> params.append(networkArgs)
>
> - allEnvs = source.get_env(args.template, args.template_dir)
> + allEnvs = source.get_env(tmpl, args.template_dir)
> envArgs = args.env
> if envArgs is not None:
> allEnvs = allEnvs + envArgs
> @@ -151,7 +150,7 @@ def run(args):
>
> def requires_template(parser):
> parser.add_argument("template",
> - help=_("name of the template"))
> + help=_("URI of the template"))
>
> def requires_name(parser):
> parser.add_argument("-n","--name",
> @@ -163,23 +162,10 @@ def check_connect(connectstr):
> raise ValueError("URI '%s' is not supported by virt-sandbox-image" % connectstr)
> return True
>
> -def requires_source(parser):
> - parser.add_argument("-s","--source",
> - default="docker",
> - help=_("name of the template"))
> -
> def requires_connect(parser):
> parser.add_argument("-c","--connect",
> help=_("Connect string for libvirt"))
>
> -def requires_auth_conn(parser):
> - parser.add_argument("-r","--registry",
> - help=_("Url of the custom registry"))
> - parser.add_argument("-u","--username",
> - help=_("Username for the custom registry"))
> - parser.add_argument("-p","--password",
> - help=_("Password for the custom registry"))
> -
> def requires_template_dir(parser):
> global default_template_dir
> parser.add_argument("-t","--template-dir",
> @@ -196,8 +182,6 @@ def gen_download_args(subparser):
> parser = subparser.add_parser("download",
> help=_("Download template data"))
> requires_template(parser)
> - requires_source(parser)
> - requires_auth_conn(parser)
> requires_template_dir(parser)
> parser.set_defaults(func=download)
>
> @@ -205,7 +189,6 @@ def gen_delete_args(subparser):
> parser = subparser.add_parser("delete",
> help=_("Delete template data"))
> requires_template(parser)
> - requires_source(parser)
> requires_template_dir(parser)
> parser.set_defaults(func=delete)
>
> @@ -213,7 +196,6 @@ def gen_create_args(subparser):
> parser = subparser.add_parser("create",
> help=_("Create image from template data"))
> requires_template(parser)
> - requires_source(parser)
> requires_connect(parser)
> requires_template_dir(parser)
> parser.add_argument("-f","--format",
> @@ -226,7 +208,6 @@ def gen_run_args(subparser):
> help=_("Run an already built image"))
> requires_name(parser)
> requires_template(parser)
> - requires_source(parser)
> requires_connect(parser)
> requires_template_dir(parser)
> requires_image_dir(parser)
> diff --git a/libvirt-sandbox/image/sources/DockerSource.py b/libvirt-sandbox/image/sources/DockerSource.py
> index c374a0c..10f8537 100644
> --- a/libvirt-sandbox/image/sources/DockerSource.py
> +++ b/libvirt-sandbox/image/sources/DockerSource.py
> @@ -2,6 +2,7 @@
> # -*- coding: utf-8 -*-
> #
> # Copyright (C) 2015 Universitat Politècnica de Catalunya.
> +# Copyright (C) 2015 Red Hat, Inc
> #
> # This library is free software; you can redistribute it and/or
> # modify it under the terms of the GNU Lesser General Public
> @@ -28,6 +29,8 @@ import traceback
> import os
> import subprocess
> import shutil
> +import urlparse
> +
>
> class DockerConfParser():
>
> @@ -47,12 +50,6 @@ class DockerConfParser():
>
> class DockerSource(Source):
>
> - www_auth_username = None
> - www_auth_password = None
> -
> - def __init__(self):
> - self.default_index_server = "index.docker.io"
> -
> def _check_cert_validate(self):
> major = sys.version_info.major
> SSL_WARNING = "SSL certificates couldn't be validated by default. You need to have 2.7.9/3.4.3 or higher"
> @@ -62,43 +59,38 @@ class DockerSource(Source):
> if (major == 2 and sys.hexversion < py2_7_9_hexversion) or (major == 3 and sys.hexversion < py3_4_3_hexversion):
> sys.stderr.write(SSL_WARNING)
>
> - def download_template(self, templatename, templatedir,
> - registry=None, username=None, password=None):
> - if registry is None:
> - registry = self.default_index_server
> -
> - if username is not None:
> - self.www_auth_username = username
> - self.www_auth_password = password
> -
> + def download_template(self, template, templatedir):
> self._check_cert_validate()
> - tag = "latest"
> - offset = templatename.find(':')
> - if offset != -1:
> - tag = templatename[offset + 1:]
> - templatename = templatename[0:offset]
> +
> try:
> - (data, res) = self._get_json(registry, "/v1/repositories/" + templatename + "/images",
> - {"X-Docker-Token": "true"})
> + (data, res) = self._get_json(template,
> + None,
> + "/v1/repositories" + template.path + "/images",
> + {"X-Docker-Token": "true"})
> except urllib2.HTTPError, e:
> - raise ValueError(["Image '%s' does not exist" % templatename])
> + raise ValueError(["Image '%s' does not exist" % template])
>
> registryendpoint = res.info().getheader('X-Docker-Endpoints')
> token = res.info().getheader('X-Docker-Token')
> checksums = {}
> for layer in data:
> pass
> - (data, res) = self._get_json(registryendpoint, "/v1/repositories/" + templatename + "/tags",
> - { "Authorization": "Token " + token })
> + (data, res) = self._get_json(template,
> + registryendpoint,
> + "/v1/repositories" + template.path + "/tags",
> + { "Authorization": "Token " + token })
>
> cookie = res.info().getheader('Set-Cookie')
>
> + tag = template.params.get("tag", "latest")
> if not tag in data:
> - raise ValueError(["Tag '%s' does not exist for image '%s'" % (tag, templatename)])
> + raise ValueError(["Tag '%s' does not exist for image '%s'" % (tag, template)])
> imagetagid = data[tag]
>
> - (data, res) = self._get_json(registryendpoint, "/v1/images/" + imagetagid + "/ancestry",
> - { "Authorization": "Token "+token })
> + (data, res) = self._get_json(template,
> + registryendpoint,
> + "/v1/images/" + imagetagid + "/ancestry",
> + { "Authorization": "Token "+ token })
>
> if data[0] != imagetagid:
> raise ValueError(["Expected first layer id '%s' to match image id '%s'",
> @@ -118,8 +110,11 @@ class DockerSource(Source):
> datafile = layerdir + "/template.tar.gz"
>
> if not os.path.exists(jsonfile) or not os.path.exists(datafile):
> - res = self._save_data(registryendpoint, "/v1/images/" + layerid + "/json",
> - { "Authorization": "Token " + token }, jsonfile)
> + res = self._save_data(template,
> + registryendpoint,
> + "/v1/images/" + layerid + "/json",
> + { "Authorization": "Token " + token },
> + jsonfile)
> createdFiles.append(jsonfile)
>
> layersize = int(res.info().getheader("Content-Length"))
> @@ -128,12 +123,15 @@ class DockerSource(Source):
> if layerid in checksums:
> datacsum = checksums[layerid]
>
> - self._save_data(registryendpoint, "/v1/images/" + layerid + "/layer",
> - { "Authorization": "Token "+token }, datafile, datacsum, layersize)
> + self._save_data(template,
> + registryendpoint,
> + "/v1/images/" + layerid + "/layer",
> + { "Authorization": "Token "+token },
> + datafile, datacsum, layersize)
> createdFiles.append(datafile)
>
> index = {
> - "name": templatename,
> + "name": template.path,
> }
>
> indexfile = templatedir + "/" + imagetagid + "/index.json"
> @@ -152,9 +150,11 @@ class DockerSource(Source):
> shutil.rmtree(d)
> except:
> pass
> - def _save_data(self,server, path, headers, dest, checksum=None, datalen=None):
> +
> + def _save_data(self, template, server, path, headers,
> + dest, checksum=None, datalen=None):
> try:
> - res = self._get_url(server, path, headers)
> + res = self._get_url(template, server, path, headers)
>
> csum = None
> if checksum is not None:
> @@ -193,8 +193,22 @@ class DockerSource(Source):
> debug("FAIL %s\n" % str(e))
> raise
>
> - def _get_url(self,server, path, headers):
> - url = "https://" + server + path
> + def _get_url(self, template, server, path, headers):
> + if template.protocol is None:
> + protocol = "https"
> + else:
> + protocol = template.protocol
> +
> + if server is None:
> + if template.hostname is None:
> + server = "index.docker.io"
> + else:
> + if template.port is not None:
> + server = template.hostname + ":" + template.port
This doesn't fly, port is an int, we need to have it in this form:
server = "%s:%d" % (template.hostname, template.port)
> + else:
> + server = template.hostname
> +
> + url = urlparse.urlunparse((protocol, server, path, None, None, None))
> debug("Fetching %s..." % url)
>
> req = urllib2.Request(url=url)
> @@ -204,16 +218,18 @@ class DockerSource(Source):
> req.add_header(h, headers[h])
>
> #www Auth header starts
> - if self.www_auth_username is not None:
> - base64string = base64.encodestring('%s:%s' % (self.www_auth_username, self.www_auth_password)).replace('\n', '')
> + if template.username and template.password:
> + base64string = base64.encodestring(
> + '%s:%s' % (template.username,
> + template.password)).replace('\n', '')
> req.add_header("Authorization", "Basic %s" % base64string)
> #www Auth header finish
>
> return urllib2.urlopen(req)
>
> - def _get_json(self,server, path, headers):
> + def _get_json(self, template, server, path, headers):
> try:
> - res = self._get_url(server, path, headers)
> + res = self._get_url(template, server, path, headers)
> data = json.loads(res.read())
> debug("OK\n")
> return (data, res)
> @@ -221,11 +237,11 @@ class DockerSource(Source):
> debug("FAIL %s\n" % str(e))
> raise
>
> - def create_template(self, templatename, templatedir, connect=None, format=None):
> + def create_template(self, template, templatedir, connect=None, format=None):
> if format is None:
> format = self.default_disk_format
> self._check_disk_format(format)
> - imagelist = self._get_image_list(templatename,templatedir)
> + imagelist = self._get_image_list(template, templatedir)
> imagelist.reverse()
>
> parentImage = None
> @@ -252,7 +268,7 @@ class DockerSource(Source):
> if not format in supportedFormats:
> raise ValueError(["Unsupported image format %s" % format])
>
> - def _get_image_list(self,templatename,destdir):
> + def _get_image_list(self, template, destdir):
> imageparent = {}
> imagenames = {}
> imagedirs = os.listdir(destdir)
> @@ -265,13 +281,13 @@ class DockerSource(Source):
> jsonfile = destdir + "/" + imagetagid + "/template.json"
> if os.path.exists(jsonfile):
> with open(jsonfile,"r") as f:
> - template = json.load(f)
> - parent = template.get("parent",None)
> + data = json.load(f)
> + parent = data.get("parent",None)
> if parent:
> imageparent[imagetagid] = parent
> - if not templatename in imagenames:
> - raise ValueError(["Image %s does not exist locally" %templatename])
> - imagetagid = imagenames[templatename]
> + if not template.path in imagenames:
> + raise ValueError(["Image %s does not exist locally" % template.path])
> + imagetagid = imagenames[template.path]
> imagelist = []
> while imagetagid != None:
> imagelist.append(imagetagid)
> @@ -310,7 +326,7 @@ class DockerSource(Source):
> cmd = cmd + params
> subprocess.call(cmd)
>
> - def delete_template(self, templatename, templatedir):
> + def delete_template(self, template, templatedir):
> imageusage = {}
> imageparent = {}
> imagenames = {}
> @@ -324,9 +340,9 @@ class DockerSource(Source):
> jsonfile = templatedir + "/" + imagetagid + "/template.json"
> if os.path.exists(jsonfile):
> with open(jsonfile,"r") as f:
> - template = json.load(f)
> + data = json.load(f)
>
> - parent = template.get("parent",None)
> + parent = data.get("parent",None)
> if parent:
> if parent not in imageusage:
> imageusage[parent] = []
> @@ -334,10 +350,10 @@ class DockerSource(Source):
> imageparent[imagetagid] = parent
>
>
> - if not templatename in imagenames:
> - raise ValueError(["Image %s does not exist locally" %templatename])
> + if not template.path in imagenames:
> + raise ValueError(["Image %s does not exist locally" % template.path])
>
> - imagetagid = imagenames[templatename]
> + imagetagid = imagenames[template.path]
> while imagetagid != None:
> debug("Remove %s\n" % imagetagid)
> parent = imageparent.get(imagetagid,None)
> @@ -360,15 +376,15 @@ class DockerSource(Source):
> parent = None
> imagetagid = parent
>
> - def _get_template_data(self, templatename, templatedir):
> - imageList = self._get_image_list(templatename, templatedir)
> + def _get_template_data(self, template, templatedir):
> + imageList = self._get_image_list(template, templatedir)
> toplayer = imageList[0]
> diskfile = templatedir + "/" + toplayer + "/template.qcow2"
> configfile = templatedir + "/" + toplayer + "/template.json"
> return configfile, diskfile
>
> - def get_disk(self,templatename, templatedir, imagedir, sandboxname):
> - configfile, diskfile = self._get_template_data(templatename, templatedir)
> + def get_disk(self, template, templatedir, imagedir, sandboxname):
> + configfile, diskfile = self._get_template_data(template, templatedir)
> tempfile = imagedir + "/" + sandboxname + ".qcow2"
> if not os.path.exists(imagedir):
> os.makedirs(imagedir)
> @@ -379,8 +395,8 @@ class DockerSource(Source):
> subprocess.call(cmd)
> return tempfile
>
> - def get_command(self, templatename, templatedir, userargs):
> - configfile, diskfile = self._get_template_data(templatename, templatedir)
> + def get_command(self, template, templatedir, userargs):
> + configfile, diskfile = self._get_template_data(template, templatedir)
> configParser = DockerConfParser(configfile)
> cmd = configParser.getCommand()
> entrypoint = configParser.getEntrypoint()
> @@ -393,8 +409,8 @@ class DockerSource(Source):
> else:
> return entrypoint + cmd
>
> - def get_env(self, templatename, templatedir):
> - configfile, diskfile = self._get_template_data(templatename, templatedir)
> + def get_env(self, template, templatedir):
> + configfile, diskfile = self._get_template_data(template, templatedir)
> configParser = DockerConfParser(configfile)
> return configParser.getEnvs()
>
> diff --git a/libvirt-sandbox/image/sources/Source.py b/libvirt-sandbox/image/sources/Source.py
> index 8a21f90..597a7fb 100644
> --- a/libvirt-sandbox/image/sources/Source.py
> +++ b/libvirt-sandbox/image/sources/Source.py
> @@ -2,6 +2,7 @@
> # -*- coding: utf-8 -*-
> #
> # Copyright (C) 2015 Universitat Politècnica de Catalunya.
> +# Copyright (C) 2015 Red Hat, Inc
> #
> # This library is free software; you can redistribute it and/or
> # modify it under the terms of the GNU Lesser General Public
> @@ -33,14 +34,10 @@ class Source():
> pass
>
> @abstractmethod
> - def download_template(self, templatename, templatedir,
> - registry=None, username=None, password=None):
> + def download_template(self, template, templatedir):
> """
> - :param templatename: name of the template image to download
> + :param template: libvirt_sandbox.template.Template object
> :param templatedir: local directory path in which to store the template
> - :param registry: optional hostname of image registry server
> - :param username: optional username to authenticate against registry server
> - :param password: optional password to authenticate against registry server
>
> Download a template from the registry, storing it in the local
> filesystem
> @@ -48,10 +45,10 @@ class Source():
> pass
>
> @abstractmethod
> - def create_template(self, templatename, templatedir,
> + def create_template(self, template, templatedir,
> connect=None, format=None):
> """
> - :param templatename: name of the template image to create
> + :param template: libvirt_sandbox.template.Template object
> :param templatedir: local directory path in which to store the template
> :param connect: libvirt connection URI
> :param format: disk image format
> @@ -63,9 +60,9 @@ class Source():
> pass
>
> @abstractmethod
> - def delete_template(self, templatename, templatedir):
> + def delete_template(self, template, templatedir):
> """
> - :param templatename: name of the template image to delete
> + :param template: libvirt_sandbox.template.Template object
> :param templatedir: local directory path from which to delete template
>
> Delete all local files associated with the template
> @@ -73,9 +70,9 @@ class Source():
> pass
>
> @abstractmethod
> - def get_command(self, templatename, templatedir, userargs):
> + def get_command(self, template, templatedir, userargs):
> """
> - :param templatename: name of the template image to query
> + :param template: libvirt_sandbox.template.Template object
> :param templatedir: local directory path in which templates are stored
> :param userargs: user specified arguments to run
>
> @@ -85,9 +82,9 @@ class Source():
> pass
>
> @abstractmethod
> - def get_disk(self,templatename, templatedir, imagedir, sandboxname):
> + def get_disk(self, template, templatedir, imagedir, sandboxname):
> """
> - :param templatename: name of the template image to download
> + :param template: libvirt_sandbox.template.Template object
> :param templatedir: local directory path in which to find template
> :param imagedir: local directory in which to storage disk image
>
> @@ -97,9 +94,9 @@ class Source():
> pass
>
> @abstractmethod
> - def get_env(self,templatename, templatedir):
> + def get_env(self, template, templatedir):
> """
> - :param templatename: name of the template image to download
> + :param template: libvirt_sandbox.template.Template object
> :param templatedir: local directory path in which to find template
>
> Get the dict of environment variables to set
> diff --git a/libvirt-sandbox/image/template.py b/libvirt-sandbox/image/template.py
> new file mode 100644
> index 0000000..0ad767b
> --- /dev/null
> +++ b/libvirt-sandbox/image/template.py
> @@ -0,0 +1,110 @@
> +#
> +# -*- coding: utf-8 -*-
> +# Authors: Daniel P. Berrange <berrange at redhat.com>
> +#
> +# Copyright (C) 2015 Red Hat, Inc.
> +#
> +# 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 2 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., 675 Mass Ave, Cambridge, MA 02139, USA.
> +#
> +
> +import urlparse
> +import importlib
> +
> +class Template(object):
> +
> + def __init__(self,
> + source, protocol,
> + hostname, port,
> + username, password,
> + path, params):
> + """
> + :param source: template source name
> + :param protocol: network transport protocol or None
> + :param hostname: registry hostname or None
> + :param port: registry port or None
> + :param username: username or None
> + :param password: password or None
> + :param path: template path identifier
> + :param params: template parameters
> +
> + docker:///ubuntu
> +
> + docker+https://index.docker.io/ubuntu?tag=latest
> + """
> +
> + self.source = source
> + self.protocol = protocol
> + self.hostname = hostname
> + self.port = port
> + self.username = username
> + self.password = password
> + self.path = path
> + self.params = params
> + if self.params is None:
> + self.params = {}
> +
> + def get_source_impl(self):
> + mod = importlib.import_module(
> + "libvirt_sandbox.image.sources." +
> + self.source.capitalize() + "Source")
> + classname = self.source.capitalize() + "Source"
> + classimpl = getattr(mod, classname)
> + return classimpl()
> +
> + def __repr__(self):
> + if self.protocol is not None:
> + scheme = self.source + "+" + self.protocol
> + else:
> + scheme = self.source
> + if self.hostname:
> + if self.port:
> + netloc = self.hostname + ":" + self.port
This doesn't work, python requires to put it this way:
netloc = "%s:%d" % (self.hostname, self.port)
--
Cedric
> + else:
> + netloc = self.hostname
> +
> + if self.username:
> + if self.password:
> + auth = self.username + ":" + self.password
> + else:
> + auth = self.username
> + netloc = auth + "@" + netloc
> + else:
> + netloc = None
> +
> + query = "&".join([key + "=" + self.params[key] for key in self.params.keys()])
> + return urlparse.urlunparse((scheme, netloc, self.path, None, query, None))
> +
> + @classmethod
> + def from_uri(klass, uri):
> + o = urlparse.urlparse(uri)
> +
> + idx = o.scheme.find("+")
> + if idx == -1:
> + source = o.scheme
> + protocol = None
> + else:
> + source = o.scheme[0:idx]
> + protocol = o.schema[idx + 1:]
s/schema/scheme/
> +
> + query = {}
> + if o.query is not None and o.query != "":
> + for param in o.query.split("&"):
> + (key, val) = param.split("=")
> + query[key] = val
> + return klass(source, protocol,
> + o.hostname, o.port,
> + o.username, o.password,
> + o.path, query)
> +
More information about the libvir-list
mailing list