Class: Visor::Image::Client

Inherits:
Object
  • Object
show all
Includes:
Common::Exception, Common::Util
Defined in:
lib/image/client.rb

Overview

Note:

In the examples presented in this page, we will consider that the VIS server is listening in the 10.0.0.1 address and port 4568. We will also use a sample user account, with access_key “foo” and secret_key “bar”.

The programming API for the VISOR Image System (VIS). This class supports all image metadata and files operations through a programming interface.

After Instantiate a VIS Client object, its possible to directly interact with the VIS server. This API conforms to the tenets of the VIS server REST API Visor Image System server.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Visor::Image::Client

Initializes a new VIS programming client. VIS server settings (host and port address) and user’s credentials should be provided for initialization or ignored (where settings will be loaded from the local VISOR configuration file).

Examples:

Instantiate a client with default values loaded from the VISOR configuration file:

client = Visor::Image::Client.new

Instantiate a client with custom host and port and with user’s credentials loaded from the VISOR configuration file:

client = Visor::Image::Client.new(host: '10.0.0.1', port: 4568)

Instantiate a client with custom host, port and user’s credentials (nothing is loaded from the VISOR configuration file):

client = Visor::Image::Client.new(host: '10.0.0.1', port: 4568, access_key: 'foo', secret_key: 'bar')

Parameters:

  • opts (Hash) (defaults to: {})

    a customizable set of options

Options Hash (opts):

  • :host (String)

    The host address where the VIS server resides.

  • :port (String)

    The host port where the VIS server listens.

  • :access_key (String)

    The user access key.

  • :secret_key (String)

    The user secret key.



42
43
44
45
46
47
48
# File 'lib/image/client.rb', line 42

def initialize(opts = {})
  configs     = Common::Config.load_config :visor_image
  @host       = opts[:host] || configs[:bind_host] || '0.0.0.0'
  @port       = opts[:port] || configs[:bind_port] || 4568
  @access_key = opts[:access_key] || configs[:access_key]
  @secret_key = opts[:secret_key] || configs[:secret_key]
end

Instance Attribute Details

#access_keyObject (readonly)

Returns the value of attribute access_key.



21
22
23
# File 'lib/image/client.rb', line 21

def access_key
  @access_key
end

#hostObject (readonly)

Returns the value of attribute host.



21
22
23
# File 'lib/image/client.rb', line 21

def host
  @host
end

#portObject (readonly)

Returns the value of attribute port.



21
22
23
# File 'lib/image/client.rb', line 21

def port
  @port
end

#secret_keyObject (readonly)

Returns the value of attribute secret_key.



21
22
23
# File 'lib/image/client.rb', line 21

def secret_key
  @secret_key
end

Instance Method Details

#delete_by_query(query) ⇒ Hash

Removes images that match a specific query.

Examples:

Delete an image by queries:

# we want to delete all 64-bit images:
query = {architecture: 'x86_64'}

# delete the image metadata and file
client.delete_by_query(query)

  # returns the matched and deleted images metadata (where in this example we had only the following 64-bit images registered):
  [
    {
     :_id          => "e5fe8ea5-4704-48f1-905a-f5747cf8ba5e",
     :uri          => "http://10.0.0.1:4568/images/e5fe8ea5-4704-48f1-905a-f5747cf8ba5e",
     :name         => "Fedora Desktop 17",
     :architecture => "x86_64",
     :access       => "public",
     :status       => "available",
     :format       => "iso",
     :size         => 676331520,
     :store        => "file",
     :location     => "file:///home/foo/VMs/e5fe8ea5-4704-48f1-905a-f5747cf8ba5e.iso",
     :created_at   => "2012-06-15 21:03:32 +0100",
     :checksum     => "330dcb53f253acdf76431cecca0fefe7",
     :owner        => "foo"
    },
    {
     :_id          => "edfa919a-0415-4d26-b54d-ae78ffc4dc79",
     :uri          => "http://10.0.0.1:4568/images/edfa919a-0415-4d26-b54d-ae78ffc4dc79",
     :name         => "Ubuntu 12.04 Server",
     :architecture => "x86_64",
     :access       => "public",
     :status       => "available",
     :format       => "iso",
     :size         => "732213248",
     :store        => "s3",
     :location     => "s3://mys3accesskey:[email protected]/mybucket/edfa919a-0415-4d26-b54d-ae78ffc4dc79.iso",
     :created_at   => "2012-06-15 21:05:20 +0100",
     :checksum     => "140f3-2ba4b000-4be8328106940",
     :owner        => "foo"
    }
  ]

Parameters:

  • query (Hash)

    A query to find images that should be deleted.

Returns:

  • (Hash)

    The already deleted image metadata. Useful for recover on accidental delete.

Raises:

  • (NotFound)

    If image meta or file were not found.

  • (Forbidden)

    If user does not have permission to manipulate the image file.

  • (Forbidden)

    If user authentication fails.

  • (InternalError)

    If VIS server was not found on the referenced host and port address.



524
525
526
527
528
529
530
531
532
# File 'lib/image/client.rb', line 524

def delete_by_query(query)
  result = []
  images = get_images(query)
  images.each do |image|
    req = Net::HTTP::Delete.new("/images/#{image[:_id]}")
    result << do_request(req)
  end
  result
end

#delete_image(id) ⇒ Hash

Removes an image record based on its _id and returns its metadata. If the image have some registered image file, that file is also deleted on its source store.

Examples:

Delete an image:

# wanted image _id
id = "edfa919a-0415-4d26-b54d-ae78ffc4dc79"

# delete the image metadata and file
client.delete_image(id)

  # returns:
  {
     :_id          => "edfa919a-0415-4d26-b54d-ae78ffc4dc79",
     :uri          => "http://10.0.0.1:4568/images/edfa919a-0415-4d26-b54d-ae78ffc4dc79",
     :name         => "Ubuntu 12.04",
     :architecture => "i386",
     :access       => "public",
     :status       => "available",
     :format       => "iso",
     :size         => 732213248,
     :store        => "http",
     :location     => "http://releases.ubuntu.com/12.04/ubuntu-12.04-desktop-amd64.iso",
     :created_at   => "2012-06-15 21:05:20 +0100",
     :updated_at   => "2012-06-15 21:10:36 +0100",
     :checksum     => "140f3-2ba4b000-4be8328106940",
     :owner        => "foo"
  }

Parameters:

  • id (String)

    The image’s _id which will be deleted.

Returns:

  • (Hash)

    The already deleted image metadata. Useful for recover on accidental delete.

Raises:

  • (NotFound)

    If image meta or file were not found.

  • (Forbidden)

    If user does not have permission to manipulate the image file.

  • (Forbidden)

    If user authentication fails.

  • (InternalError)

    If VIS server was not found on the referenced host and port address.



467
468
469
470
# File 'lib/image/client.rb', line 467

def delete_image(id)
  req = Net::HTTP::Delete.new("/images/#{id}")
  do_request(req)
end

#get_image(id) ⇒ Binary

Retrieves the file of the image with the given id.

The file is yielded in streaming chunks, so its possible to receive a big file without buffering it all in memory.

Examples:

Retrieve the image file with _id value:

# wanted image _id
id = "5e47a41e-7b94-4f65-824e-28f94e15bc6a"

# ask for that image file
client.get_image(id) do |chunk|
  # do something with chunks as they arrive here (e.g. write to file, etc)
end

Parameters:

  • id (String)

    The wanted image’s _id.

Returns:

  • (Binary)

    The requested image file binary data.

Raises:

  • (NotFound)

    If image not found.

  • (Forbidden)

    If user authentication fails.

  • (InternalError)

    If VIS server was not found on the referenced host and port address.



184
185
186
187
188
189
190
191
192
193
194
# File 'lib/image/client.rb', line 184

def get_image(id)
  req = Net::HTTP::Get.new("/images/#{id}")
  prepare_headers(req)

  Net::HTTP.start(host, port) do |http|
    http.request(req) do |res|
      assert_response(res)
      res.read_body { |chunk| yield chunk }
    end
  end
end

#get_images(query = {}) ⇒ Array

Retrieves brief metadata of all public and user’s private images. Options for filtering the returned results can be passed in.

Examples:

Retrieve all images brief metadata:

client.get_images

  # returns:
  [<all images brief metadata>]

Retrieve all 32bit images brief metadata:

client.get_images(architecture: 'i386')

  # returns something like:
  [
    {:_id => "28f94e15...", :architecture => "i386", :name => "Fedora 16"},
    {:_id => "8cb55bb6...", :architecture => "i386", :name => "Ubuntu 11.10 Desktop"}
  ]

Retrieve all 64bit images brief metadata, descending sorted by their name:

client.get_images(architecture: 'x86_64', sort: 'name', dir: 'desc')

  # returns something like:
  [
    {:_id => "5e47a41e...", :architecture => "x86_64", :name => "Ubuntu 10.04 Server"},
    {:_id => "069320f0...", :architecture => "x86_64", :name => "CentOS 6"}
  ]

Parameters:

  • query (Hash) (defaults to: {})

    a customizable set of options

Options Hash (query):

  • :attribute (String)

    The image attribute value to filter returned results.

  • :sort (String) — default: "_id"

    The image attribute to sort returned results.

  • :dir (String) — default: "asc"

    The direction to sort results (“asc”/“desc”).

Returns:

  • (Array)

    All images brief metadata. Just BRIEF fields are returned.

Raises:

  • (NotFound)

    If there are no images registered on VISOR.

  • (Forbidden)

    If user authentication fails.

  • (InternalError)

    If VIS server was not found on the referenced host and port address.



129
130
131
132
133
# File 'lib/image/client.rb', line 129

def get_images(query = {})
  str = build_query(query)
  req = Net::HTTP::Get.new("/images#{str}")
  do_request(req)
end

#get_images_detail(query = {}) ⇒ Array

Note:

Filtering and querying works the same as with #get_images. The only difference is the number

Retrieves detailed metadata of all public and user’s private images.

of disclosed attributes.

Examples:

Retrieve all images detailed metadata:

# request for it
client.get_images_detail
# returns an array of hashes with all images detailed metadata.

Parameters:

  • query (Hash) (defaults to: {})

    a customizable set of options

Options Hash (query):

  • :attribute_name (String)

    The image attribute value to filter returned results.

  • :sort (String) — default: "_id"

    The image attribute to sort returned results.

  • :dir (String) — default: "asc"

    The direction to sort results (“asc”/“desc”).

Returns:

  • (Array)

    All images detailed metadata. The DETAIL_EXC fields are excluded from results.

Raises:

  • (NotFound)

    If there are no images registered on VISOR.

  • (Forbidden)

    If user authentication fails.

  • (InternalError)

    If VIS server was not found on the referenced host and port address.



156
157
158
159
160
# File 'lib/image/client.rb', line 156

def get_images_detail(query = {})
  str = build_query(query)
  req = Net::HTTP::Get.new("/images/detail#{str}")
  do_request(req)
end

#head_image(id) ⇒ Hash

Retrieves detailed image metadata of the image with the given id.

Examples:

Retrieve the image metadata with _id value:

# wanted image _id
id = "5e47a41e-7b94-4f65-824e-28f94e15bc6a"

# ask for that image metadata
client.head_image(id)

  # return example:
  {
     :_id          => "edfa919a-0415-4d26-b54d-ae78ffc4dc79",
     :uri          => "http://10.0.0.1:4568/images/edfa919a-0415-4d26-b54d-ae78ffc4dc79",
     :name         => "Ubuntu 12.04 Server",
     :architecture => "x86_64",
     :access       => "public",
     :status       => "available",
     :format       => "iso",
     :size         => "732213248",
     :store        => "s3",
     :location     => "s3://mys3accesskey:[email protected]/mybucket/edfa919a-0415-4d26-b54d-ae78ffc4dc79.iso",
     :created_at   => "2012-06-15 21:05:20 +0100",
     :checksum     => "140f3-2ba4b000-4be8328106940",
     :owner        => "foo"
  }

Parameters:

  • id (String)

    The wanted image’s _id.

Returns:

  • (Hash)

    The requested image metadata.

Raises:

  • (NotFound)

    If image not found.

  • (Forbidden)

    If user authentication fails.

  • (InternalError)

    If VIS server was not found on the referenced host and port address.



84
85
86
87
88
89
# File 'lib/image/client.rb', line 84

def head_image(id)
  path = "/images/#{id}"
  req  = Net::HTTP::Head.new(path)
  res  = do_request(req, false)
  pull_meta_from_headers(res)
end

#post_image(meta, file = nil) ⇒ Hash

Note:

If the :location parameter is passed, you can not pass an image file and the other way around too.

Register a new image on VISOR with the given metadata and optionally upload its file, or provide a :location parameter containing the full path to the already existing image file, stored somewhere.

The image file is streamed to the server in chunks, which in turn also buffers chunks as they arrive, avoiding buffering large files in memory in both clients and server.

Examples:

Insert a sample image metadata:

# sample image metadata
meta = {:name => 'CentOS 6.2', :architecture => 'i386', :format => 'iso', :access => 'private'}

# insert the new image metadata
client.post_image(meta)

  # returns:
  {
     :_id          => "7583d669-8a65-41f1-b8ae-eb34ff6b322f",
     :uri          => "http://10.0.0.1:4568/images/7583d669-8a65-41f1-b8ae-eb34ff6b322f",
     :name         => "CentOS 6.2",
     :architecture => "i386",
     :access       => "private",
     :status       => "locked",
     :format       => "iso",
     :created_at   => "2012-06-15 21:01:21 +0100",
     :owner        => "foo"
  }

Insert a sample image metadata and provide the location of its file:

# sample image pointing to the latest release of Ubuntu Server distro
meta = {:name  => 'Ubuntu 12.04 Server', :architecture => 'x86_64', :format => 'iso',
        :store => 'http', :location => 'http://releases.ubuntu.com/12.04/ubuntu-12.04-desktop-amd64.iso'}

# insert the new image metadata
client.post_image(meta)

  # returns:
  {
     :_id          => "edfa919a-0415-4d26-b54d-ae78ffc4dc79",
     :uri          => "http://10.0.0.1:4568/images/edfa919a-0415-4d26-b54d-ae78ffc4dc79",
     :name         => "Ubuntu 12.04 Server",
     :architecture => "x86_64",
     :access       => "public",
     :status       => "available",
     :format       => "iso",
     :size         => 732213248, # it will find the remote file size
     :store        => "http",
     :location     => "http://releases.ubuntu.com/12.04/ubuntu-12.04-desktop-amd64.iso",
     :created_at   => "2012-06-15 21:05:20 +0100",
     :checksum     => "140f3-2ba4b000-4be8328106940", # it will also find the remote file checksum or etag
     :owner        => "foo"
  }

Insert a sample image metadata and upload its file:

# sample image metadata
meta = {:name => 'Fedora Desktop 17', :architecture => 'x86_64', :format => 'iso', :store => 'file'}

# sample image file path
file = '~/Fedora-17-x86_64-Live-Desktop.iso'

# insert the new image metadata and upload file
client.post_image(meta, file)

  # returns:
  {
     :_id          => "e5fe8ea5-4704-48f1-905a-f5747cf8ba5e",
     :uri          => "http://10.0.0.1:4568/images/e5fe8ea5-4704-48f1-905a-f5747cf8ba5e",
     :name         => "Fedora Desktop 17",
     :architecture => "x86_64",
     :access       => "public",
     :status       => "available",
     :format       => "iso",
     :size         => 676331520,
     :store        => "file",
     :location     => "file:///home/foo/VMs/e5fe8ea5-4704-48f1-905a-f5747cf8ba5e.iso",
     :created_at   => "2012-06-15 21:03:32 +0100",
     :checksum     => "330dcb53f253acdf76431cecca0fefe7",
     :owner        => "foo"
  }

Parameters:

  • meta (Hash)

    The image metadata.

  • file (String) (defaults to: nil)

    The path to the image file.

Returns:

  • (Hash)

    The already inserted image metadata.

Raises:

  • (Invalid)

    If image metadata validation fails.

  • (Invalid)

    If the location header is present no file content can be provided.

  • (Invalid)

    If trying to post an image file to a HTTP backend.

  • (Invalid)

    If provided store is an unsupported store backend.

  • (NotFound)

    If no image file is found at the provided location.

  • (ConflictError)

    If the provided image file already exists in the target backend store.

  • (Forbidden)

    If user authentication fails.

  • (InternalError)

    If VIS server was not found on the referenced host and port address.



292
293
294
295
296
297
298
299
300
301
# File 'lib/image/client.rb', line 292

def post_image(meta, file = nil)
  req = Net::HTTP::Post.new('/images')
  push_meta_into_headers(meta, req)
  if file
    req['Content-Type']      = 'application/octet-stream'
    req['Transfer-Encoding'] = 'chunked'
    req.body_stream          = File.open(File.expand_path file)
  end
  do_request(req)
end

#put_image(id, meta, file = nil) ⇒ Hash

Note:

Only images with status set to ‘locked’ or ‘error’ can be updated with an image data file.

Updates an image record with the given metadata and optionally upload its file, or provide a :location parameter containing the full path to the already existing image file, stored somewhere.

The image file is streamed to the server in chunks, which in turn also buffers chunks as they arrive, avoiding buffering large files in memory in both clients and server.

the image file was already stored in the local filesystem backend, thus it is not needed to upload the file, but rather just register that the image file is there.

# wanted image _id
id = "7583d669-8a65-41f1-b8ae-eb34ff6b322f"

# metadata update
update = {:format => 'iso', :store => 'file', :location => 'file:///Users/server/debian-6.0.4-amd64.iso'}

# update the image metadata with file values
client.put_image(id, update)

  # returns:
  {
     :_id          => "7583d669-8a65-41f1-b8ae-eb34ff6b322f",
     :uri          => "http://10.0.0.1:4568/images/7583d669-8a65-41f1-b8ae-eb34ff6b322f",
     :name         => "CentOS 6.2",
     :architecture => "i386",
     :access       => "private",
     :status       => "available",
     :format       => "iso",
     :size         => 729808896,
     :store        => "file",
     :location     => "file:///home/foo/downloads/CentOS-6.2-i386-LiveCD.iso",
     :created_at   => "2012-06-15 21:01:21 +0100",
     :updated_at   => "2012-06-15 21:12:27 +0100",
     :checksum     => "1b8441b6f4556be61c16d9750da42b3f",
     :owner        => "foo"
  }

Examples:

Update a sample image metadata:

# wanted image _id
id = "edfa919a-0415-4d26-b54d-ae78ffc4dc79."

# metadata to update
update = {:name => 'Ubuntu 12.04', :architecture => "i386"}

# update the image metadata with some new values
client.put_image(id, update)

  # returns:
  {
     :_id          => "edfa919a-0415-4d26-b54d-ae78ffc4dc79",
     :uri          => "http://10.0.0.1:4568/images/edfa919a-0415-4d26-b54d-ae78ffc4dc79",
     :name         => "Ubuntu 12.04",
     :architecture => "i386",
     :access       => "public",
     :status       => "available",
     :format       => "iso",
     :size         => 732213248,
     :store        => "http",
     :location     => "http://releases.ubuntu.com/12.04/ubuntu-12.04-desktop-amd64.iso",
     :created_at   => "2012-06-15 21:05:20 +0100",
     :updated_at   => "2012-06-15 21:10:36 +0100",
     :checksum     => "140f3-2ba4b000-4be8328106940",
     :owner        => "foo"
  }

Update the image metadata and provide the location of its file. In this example,

OR update image metadata and upload its file, if it is not already in some compatible storage backend:

# wanted image _id
id = "7583d669-8a65-41f1-b8ae-eb34ff6b322f"

# metadata update
update = {:format => 'iso', :store => 'file'}

# sample image file path
file = '~/CentOS-6.2-i386-LiveCD.iso'

# insert the new image metadata and upload file
client.put_image(id, meta, file)

  # returns:
  {
     :_id          => "7583d669-8a65-41f1-b8ae-eb34ff6b322f",
     :uri          => "http://10.0.0.1:4568/images/7583d669-8a65-41f1-b8ae-eb34ff6b322f",
     :name         => "CentOS 6.2",
     :architecture => "i386",
     :access       => "private",
     :status       => "available",
     :format       => "iso",
     :size         => 729808896,
     :store        => "file",
     :location     => "file:///home/foo/VMs/7583d669-8a65-41f1-b8ae-eb34ff6b322f.iso",
     :created_at   => "2012-06-15 21:01:21 +0100",
     :updated_at   => "2012-06-15 21:12:27 +0100",
     :checksum     => "1b8441b6f4556be61c16d9750da42b3f",
     :owner        => "foo"
  }

Parameters:

  • id (String)

    The image’s _id which will be updated.

  • meta (Hash)

    The image metadata.

  • file (String) (defaults to: nil)

    The path to the image file.

Returns:

  • (Hash)

    The already inserted image metadata.

Raises:

  • (Invalid)

    If the image metadata validation fails.

  • (Invalid)

    If no headers neither body found for update.

  • (Invalid)

    If the location header is present no file content can be provided.

  • (Invalid)

    If trying to post an image file to a HTTP backend.

  • (Invalid)

    If provided store is an unsupported store backend.

  • (NotFound)

    If no image data is found at the provided location.

  • (ConflictError)

    If trying to assign image file to a locked or uploading image.

  • (ConflictError)

    If the provided image file already exists in the backend store.

  • (Forbidden)

    If user authentication fails.

  • (InternalError)

    If VIS server was not found on the referenced host and port address.



419
420
421
422
423
424
425
426
427
428
# File 'lib/image/client.rb', line 419

def put_image(id, meta, file = nil)
  req = Net::HTTP::Put.new("/images/#{id}")
  push_meta_into_headers(meta, req) if meta
  if file
    req['Content-Type']      = 'application/octet-stream'
    req['Transfer-Encoding'] = 'chunked'
    req.body_stream          = File.open(File.expand_path file)
  end
  do_request(req)
end