Re: [PATCH v2 sandbox] virt-sandbox-image: switch to use URI to identify templates

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 2015-09-21 at 22:11 +0200, Cedric Bosdonnat wrote:
> 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@xxxxxxxxxxxxxxx/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 ++++++++++++++++++++
> 
> Missing change in libvirt-sandbox/image/Makefile.am to add template.py.
> As is that file isn't installed.
> 
> I'm also just realizing that we didn't add Eren't commit for the
> virt-sandbox-image man page. Adding it later is fine, but we need to
> keep that on our radar.
> 
> >  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@xxxxxxxxxx>
> >  #          Eren Yagdiran <erenyagdiran@xxxxxxxxx>
> >  #
> > -# 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

This IOError exception message should be rephrased or handled with the
general Exception.

--
Cedric

> >      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"))
> 
> Shouldn't we provide some examples here? As those URIs can't be invented
> we need to give the user some chances to discover them without having to
> read our code ;)
> 
> >  
> >  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
> > +                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@xxxxxxxxxx>
> > +#
> > +# 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
> > +            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:]
> > +
> > +        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)
> > +
> 
> git complains about the empty line here.
> 
> ACK, with the help improvement + Makefile.am fix.
> 
> --
> Cedric
> 
> --
> libvir-list mailing list
> libvir-list@xxxxxxxxxx
> https://www.redhat.com/mailman/listinfo/libvir-list


--
libvir-list mailing list
libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]