Update to new code and tests based on changes from upstream fog.
This commit is contained in:
parent
03bf99d9d3
commit
8fca81df40
|
@ -1,11 +1,15 @@
|
|||
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'hp'))
|
||||
require 'fog/hp'
|
||||
|
||||
module Fog
|
||||
module HP
|
||||
class BlockStorage < Fog::Service
|
||||
|
||||
requires :hp_secret_key, :hp_account_id, :hp_tenant_id, :hp_avl_zone
|
||||
recognizes :hp_auth_uri, :persistent, :connection_options, :hp_use_upass_auth_style, :hp_auth_version, :user_agent
|
||||
recognizes :hp_auth_uri
|
||||
recognizes :persistent, :connection_options
|
||||
recognizes :hp_use_upass_auth_style, :hp_auth_version, :user_agent
|
||||
|
||||
secrets :hp_secret_key
|
||||
|
||||
model_path 'fog/hp/models/block_storage'
|
||||
model :volume
|
||||
|
@ -78,7 +82,6 @@ module Fog
|
|||
include Utils
|
||||
|
||||
def initialize(options={})
|
||||
require 'multi_json'
|
||||
@hp_secret_key = options[:hp_secret_key]
|
||||
@hp_account_id = options[:hp_account_id]
|
||||
@hp_auth_uri = options[:hp_auth_uri]
|
||||
|
@ -138,7 +141,7 @@ module Fog
|
|||
end
|
||||
end
|
||||
if !response.body.empty? && parse_json && response.headers['Content-Type'] =~ %r{application/json}
|
||||
response.body = MultiJson.decode(response.body)
|
||||
response.body = Fog::JSON.decode(response.body)
|
||||
end
|
||||
response
|
||||
end
|
||||
|
|
|
@ -10,12 +10,12 @@ module Fog
|
|||
model Fog::HP::BlockStorage::Volume
|
||||
|
||||
def all
|
||||
data = connection.list_bootable_volumes.body['volumes']
|
||||
data = service.list_bootable_volumes.body['volumes']
|
||||
load(data)
|
||||
end
|
||||
|
||||
def get(volume_id)
|
||||
volume = connection.get_bootable_volume_details(volume_id).body['volume']
|
||||
volume = service.get_bootable_volume_details(volume_id).body['volume']
|
||||
new(volume)
|
||||
rescue Fog::HP::BlockStorage::NotFound
|
||||
nil
|
||||
|
|
|
@ -17,14 +17,14 @@ module Fog
|
|||
#attribute :metadata
|
||||
|
||||
def initialize(attributes = {})
|
||||
# assign these attributes first to prevent race condition with new_record?
|
||||
# assign these attributes first to prevent race condition with persisted?
|
||||
self.force = attributes.delete(:force) # force snapshotting of attached volumes
|
||||
super
|
||||
end
|
||||
|
||||
def destroy
|
||||
requires :id
|
||||
connection.delete_snapshot(id)
|
||||
service.delete_snapshot(id)
|
||||
true
|
||||
end
|
||||
|
||||
|
@ -37,14 +37,14 @@ module Fog
|
|||
end
|
||||
|
||||
def save
|
||||
raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if identity
|
||||
raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if persisted?
|
||||
requires :name, :volume_id
|
||||
options = {
|
||||
#'metadata' => metadata, # TODO: Add metadata when snapshots support it
|
||||
'force' => @force
|
||||
}
|
||||
options = options.reject {|key, value| value.nil?}
|
||||
data = connection.create_snapshot(name, description, volume_id, options)
|
||||
data = service.create_snapshot(name, description, volume_id, options)
|
||||
merge_attributes(data.body['snapshot'])
|
||||
true
|
||||
end
|
||||
|
|
|
@ -10,12 +10,12 @@ module Fog
|
|||
model Fog::HP::BlockStorage::Snapshot
|
||||
|
||||
def all
|
||||
data = connection.list_snapshots.body['snapshots']
|
||||
data = service.list_snapshots.body['snapshots']
|
||||
load(data)
|
||||
end
|
||||
|
||||
def get(snapshot_id)
|
||||
if snapshot = connection.get_snapshot_details(snapshot_id).body['snapshot']
|
||||
if snapshot = service.get_snapshot_details(snapshot_id).body['snapshot']
|
||||
new(snapshot)
|
||||
end
|
||||
rescue Fog::HP::BlockStorage::NotFound
|
||||
|
|
|
@ -26,7 +26,6 @@ module Fog
|
|||
def initialize(attributes = {})
|
||||
# assign these attributes first to prevent race condition with new_record?
|
||||
self.image_id = attributes.delete(:image_id)
|
||||
@connection = attributes[:connection]
|
||||
super
|
||||
end
|
||||
|
||||
|
@ -60,7 +59,7 @@ module Fog
|
|||
def attach(new_server_id, device)
|
||||
requires :id
|
||||
unless in_use?
|
||||
data = connection.compute.attach_volume(new_server_id, id, device)
|
||||
data = service.compute.attach_volume(new_server_id, id, device)
|
||||
merge_attributes(:attachments => attachments << data.body['volumeAttachment'])
|
||||
true
|
||||
else
|
||||
|
@ -71,19 +70,19 @@ module Fog
|
|||
def detach
|
||||
requires :id
|
||||
if has_attachments?
|
||||
connection.compute.detach_volume(server_id, id)
|
||||
service.compute.detach_volume(server_id, id)
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
def destroy
|
||||
requires :id
|
||||
connection.delete_volume(id)
|
||||
service.delete_volume(id)
|
||||
true
|
||||
end
|
||||
|
||||
def save
|
||||
raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if identity
|
||||
raise Fog::Errors::Error.new('Resaving an existing object may create a duplicate') if persisted?
|
||||
requires :name, :size
|
||||
options = {
|
||||
'metadata' => metadata,
|
||||
|
@ -91,7 +90,7 @@ module Fog
|
|||
'imageRef' => @image_id
|
||||
}
|
||||
options = options.reject {|key, value| value.nil?}
|
||||
data = connection.create_volume(name, description, size, options)
|
||||
data = service.create_volume(name, description, size, options)
|
||||
merge_attributes(data.body['volume'])
|
||||
true
|
||||
end
|
||||
|
|
|
@ -10,12 +10,12 @@ module Fog
|
|||
model Fog::HP::BlockStorage::Volume
|
||||
|
||||
def all
|
||||
data = connection.list_volumes.body['volumes']
|
||||
data = service.list_volumes.body['volumes']
|
||||
load(data)
|
||||
end
|
||||
|
||||
def get(volume_id)
|
||||
volume = connection.get_volume_details(volume_id).body['volume']
|
||||
volume = service.get_volume_details(volume_id).body['volume']
|
||||
new(volume)
|
||||
rescue Fog::HP::BlockStorage::NotFound
|
||||
nil
|
||||
|
|
|
@ -13,13 +13,13 @@ module Fog
|
|||
|
||||
def destroy
|
||||
requires :identity
|
||||
connection.delete_meta(collection_name, @parent.id, key)
|
||||
service.delete_meta(collection_name, @parent.id, key)
|
||||
true
|
||||
end
|
||||
|
||||
def save
|
||||
requires :identity, :value
|
||||
connection.update_meta(collection_name, @parent.id, key, value)
|
||||
service.update_meta(collection_name, @parent.id, key, value)
|
||||
true
|
||||
end
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ module Fog
|
|||
def all
|
||||
requires :parent
|
||||
if @parent.id
|
||||
metadata = connection.list_metadata(collection_name, @parent.id).body['metadata']
|
||||
metadata = service.list_metadata(collection_name, @parent.id).body['metadata']
|
||||
metas = []
|
||||
metadata.each_pair {|k,v| metas << {"key" => k, "value" => v} }
|
||||
load(metas)
|
||||
|
@ -26,14 +26,14 @@ module Fog
|
|||
|
||||
def destroy(key)
|
||||
requires :parent
|
||||
connection.delete_meta(collection_name, @parent.id, key)
|
||||
service.delete_meta(collection_name, @parent.id, key)
|
||||
rescue Fog::Compute::HP::NotFound
|
||||
nil
|
||||
end
|
||||
|
||||
def get(key)
|
||||
requires :parent
|
||||
data = connection.get_meta(collection_name, @parent.id, key).body["meta"]
|
||||
data = service.get_meta(collection_name, @parent.id, key).body["meta"]
|
||||
metas = []
|
||||
data.each_pair {|k,v| metas << {"key" => k, "value" => v} }
|
||||
new(metas[0])
|
||||
|
@ -48,12 +48,12 @@ module Fog
|
|||
|
||||
def set(data=nil)
|
||||
requires :parent
|
||||
connection.set_metadata(collection_name, @parent.id, meta_hash(data))
|
||||
service.set_metadata(collection_name, @parent.id, meta_hash(data))
|
||||
end
|
||||
|
||||
def update(data=nil)
|
||||
requires :parent
|
||||
connection.update_metadata(collection_name, @parent.id, meta_hash(data))
|
||||
service.update_metadata(collection_name, @parent.id, meta_hash(data))
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ module Fog
|
|||
end
|
||||
|
||||
def head(url)
|
||||
data = connection.head_shared_container(url)
|
||||
data = service.head_shared_container(url)
|
||||
shared_directory = new(:url => url)
|
||||
for key, value in data.headers
|
||||
if ['X-Container-Bytes-Used', 'X-Container-Object-Count'].include?(key)
|
||||
|
@ -29,7 +29,7 @@ module Fog
|
|||
end
|
||||
|
||||
def get(url)
|
||||
data = connection.get_shared_container(url)
|
||||
data = service.get_shared_container(url)
|
||||
shared_directory = new(:url => url)
|
||||
for key, value in data.headers
|
||||
if ['X-Container-Bytes-Used', 'X-Container-Object-Count'].include?(key)
|
||||
|
|
|
@ -16,7 +16,7 @@ module Fog
|
|||
@files ||= begin
|
||||
Fog::Storage::HP::SharedFiles.new(
|
||||
:shared_directory => self,
|
||||
:connection => connection
|
||||
:service => service
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,7 +32,7 @@ module Fog
|
|||
|
||||
def destroy
|
||||
requires :shared_directory, :key
|
||||
connection.delete_shared_object(self.url)
|
||||
service.delete_shared_object(self.url)
|
||||
true
|
||||
# throws exception Fog::HP::Errors::Forbidden if insufficient access
|
||||
rescue Fog::Storage::HP::NotFound
|
||||
|
@ -46,7 +46,7 @@ module Fog
|
|||
def save(options = {})
|
||||
requires :shared_directory, :key
|
||||
options['Content-Type'] = content_type if content_type
|
||||
data = connection.put_shared_object(shared_directory.url, key, body, options)
|
||||
data = service.put_shared_object(shared_directory.url, key, body, options)
|
||||
merge_attributes(data.headers)
|
||||
self.content_length = Fog::Storage.get_body_size(body)
|
||||
true
|
||||
|
|
|
@ -27,7 +27,7 @@ module Fog
|
|||
def get(key, &block)
|
||||
requires :shared_directory
|
||||
shared_object_url = "#{shared_directory.url}/#{key}"
|
||||
data = connection.get_shared_object(shared_object_url, &block)
|
||||
data = service.get_shared_object(shared_object_url, &block)
|
||||
file_data = data.headers.merge({
|
||||
:body => data.body,
|
||||
:key => key
|
||||
|
@ -41,7 +41,7 @@ module Fog
|
|||
def head(key)
|
||||
requires :shared_directory
|
||||
shared_object_url = "#{shared_directory.url}/#{key}"
|
||||
data = connection.head_shared_object(shared_object_url)
|
||||
data = service.head_shared_object(shared_object_url)
|
||||
file_data = data.headers.merge({
|
||||
:body => '',
|
||||
:key => key
|
||||
|
|
|
@ -38,7 +38,7 @@ module Fog
|
|||
end
|
||||
|
||||
request(
|
||||
:body => MultiJson.encode(data),
|
||||
:body => Fog::JSON.encode(data),
|
||||
:expects => 200,
|
||||
:method => 'POST',
|
||||
:path => "os-snapshots"
|
||||
|
|
|
@ -44,7 +44,7 @@ module Fog
|
|||
end
|
||||
|
||||
request(
|
||||
:body => MultiJson.encode(data),
|
||||
:body => Fog::JSON.encode(data),
|
||||
:expects => 200,
|
||||
:method => 'POST',
|
||||
:path => "os-volumes"
|
||||
|
|
|
@ -24,7 +24,7 @@ module Fog
|
|||
}
|
||||
}
|
||||
response = request(
|
||||
:body => MultiJson.encode(data),
|
||||
:body => Fog::JSON.encode(data),
|
||||
:expects => 200,
|
||||
:method => 'POST',
|
||||
:path => "servers/#{server_id}/os-volume_attachments"
|
||||
|
|
|
@ -107,7 +107,7 @@ module Fog
|
|||
end
|
||||
|
||||
request(
|
||||
:body => MultiJson.encode(data),
|
||||
:body => Fog::JSON.encode(data),
|
||||
:expects => 200,
|
||||
:method => 'POST',
|
||||
:path => 'os-volumes_boot'
|
||||
|
|
|
@ -17,7 +17,7 @@ module Fog
|
|||
#
|
||||
def set_metadata(collection_name, parent_id, metadata = {})
|
||||
request(
|
||||
:body => MultiJson.encode({ 'metadata' => metadata }),
|
||||
:body => Fog::JSON.encode({ 'metadata' => metadata }),
|
||||
:expects => 200,
|
||||
:method => 'PUT',
|
||||
:path => "#{collection_name}/#{parent_id}/metadata"
|
||||
|
|
|
@ -18,7 +18,7 @@ module Fog
|
|||
#
|
||||
def update_meta(collection_name, parent_id, key, value)
|
||||
request(
|
||||
:body => MultiJson.encode({ 'meta' => { key => value }}),
|
||||
:body => Fog::JSON.encode({ 'meta' => { key => value }}),
|
||||
:expects => 200,
|
||||
:method => 'PUT',
|
||||
:path => "#{collection_name}/#{parent_id}/metadata/#{key}"
|
||||
|
|
|
@ -17,7 +17,7 @@ module Fog
|
|||
#
|
||||
def update_metadata(collection_name, parent_id, metadata = {})
|
||||
request(
|
||||
:body => MultiJson.encode({ 'metadata' => metadata }),
|
||||
:body => Fog::JSON.encode({ 'metadata' => metadata }),
|
||||
:expects => 200,
|
||||
:method => 'POST',
|
||||
:path => "#{collection_name}/#{parent_id}/metadata.json"
|
||||
|
|
|
@ -17,6 +17,7 @@ Shindo.tests("Fog::Compute[:hp] | metadata for images", ['hp']) do
|
|||
end
|
||||
|
||||
tests("#get('Meta1')").succeeds do
|
||||
pending if Fog.mocking?
|
||||
@image.metadata.get('Meta1')
|
||||
end
|
||||
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
Shindo.tests('Fog::Storage[:hp] | directories', ['hp', 'storage']) do
|
||||
Shindo.tests("Fog::Storage[:hp] | directories", ['hp', 'storage']) do
|
||||
|
||||
collection_tests(Fog::Storage[:hp].directories, {:key => "fogdirtests"}, true)
|
||||
|
||||
tests('success') do
|
||||
|
||||
tests("#create('fogdirtests')").succeeds do
|
||||
Fog::Storage[:hp].directories.create(:key => 'fogdirtests')
|
||||
end
|
||||
|
||||
tests("#head('fogdirtests')").succeeds do
|
||||
Fog::Storage[:hp].directories.head('fogdirtests')
|
||||
end
|
||||
|
||||
tests("#get('fogdirtests')").succeeds do
|
||||
Fog::Storage[:hp].directories.get('fogdirtests')
|
||||
@directory = Fog::Storage[:hp].directories.get('fogdirtests')
|
||||
end
|
||||
|
||||
@directory.destroy
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
Shindo.tests('Fog::Storage[:hp] | directory', ['hp', 'storage']) do
|
||||
Shindo.tests("Fog::Storage[:hp] | directory", ['hp', 'storage']) do
|
||||
|
||||
model_tests(Fog::Storage[:hp].directories, {:key => "fogdirtests"}, true) do
|
||||
|
||||
|
@ -25,6 +25,7 @@ Shindo.tests('Fog::Storage[:hp] | directory', ['hp', 'storage']) do
|
|||
@instance.files.get('sample.txt').destroy
|
||||
|
||||
tests("#cdn_enable=(true)").succeeds do
|
||||
pending if Fog.mocking?
|
||||
@instance.cdn_enable=(true)
|
||||
tests("cdn_enabled?").returns(true) do
|
||||
pending if Fog.mocking?
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Shindo.tests('Fog::Storage[:hp] | directory', ['hp', 'storage']) do
|
||||
Shindo.tests("Fog::Storage[:hp] | directory", ['hp', 'storage']) do
|
||||
|
||||
file_attributes = {
|
||||
:key => 'fog_file_tests',
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Shindo.tests('Fog::Storage[:hp] | files', ['hp', 'storage']) do
|
||||
Shindo.tests("Fog::Storage[:hp] | files", ['hp', 'storage']) do
|
||||
|
||||
file_attributes = {
|
||||
:key => 'fog_files_tests',
|
||||
|
@ -9,34 +9,30 @@ Shindo.tests('Fog::Storage[:hp] | files', ['hp', 'storage']) do
|
|||
:key => 'fogfilestests'
|
||||
}
|
||||
|
||||
@directory = Fog::Storage[:hp].directories.create(directory_attributes)
|
||||
collection_tests(Fog::Storage[:hp].directories.create(directory_attributes).files, file_attributes, true)
|
||||
|
||||
collection_tests(@directory.files, file_attributes, true) do
|
||||
@directory = Fog::Storage[:hp].directories.create(directory_attributes)
|
||||
@file = @directory.files.create(file_attributes)
|
||||
|
||||
tests('success') do
|
||||
|
||||
tests("#get_url('#{@directory.key}')").succeeds do
|
||||
@directory.files.get_url("#{@directory.key}")
|
||||
@directory.files.get_url(@directory.key)
|
||||
end
|
||||
|
||||
tests("#get_cdn_url('#{@directory.key}')").succeeds do
|
||||
pending if Fog.mocking?
|
||||
@directory.files.get_cdn_url("#{@directory.key}")
|
||||
@directory.files.get_cdn_url(@directory.key)
|
||||
end
|
||||
|
||||
tests("#get_cdn_ssl_url('#{@directory.key}')").succeeds do
|
||||
pending if Fog.mocking?
|
||||
@directory.files.get_cdn_ssl_url("#{@directory.key}")
|
||||
end
|
||||
|
||||
tests("#head('#{@directory.key}')").succeeds do
|
||||
@directory.files.head("#{@directory.key}")
|
||||
end
|
||||
|
||||
@directory.files.get_cdn_ssl_url(@directory.key)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@file.destroy
|
||||
@directory.destroy
|
||||
|
||||
end
|
|
@ -26,7 +26,7 @@ Shindo.tests("HP::BlockStorage | volume requests", ['hp', 'block_storage', 'volu
|
|||
@volume_desc = @volume_name + " desc"
|
||||
@base_image_id = ENV["BASE_IMAGE_ID"] || 1242
|
||||
|
||||
@server = HP[:block_storage].compute.servers.create(:name => 'fogvoltests', :flavor_id => 100, :image_id => @base_image_id)
|
||||
@server = Fog::Compute[:hp].servers.create(:name => 'fogvoltests', :flavor_id => 100, :image_id => @base_image_id)
|
||||
@server.wait_for { ready? }
|
||||
|
||||
tests("#create_volume(#{@volume_name}, #{@volume_desc}, 1)").formats(@volume_format) do
|
||||
|
@ -46,12 +46,12 @@ Shindo.tests("HP::BlockStorage | volume requests", ['hp', 'block_storage', 'volu
|
|||
|
||||
HP[:block_storage].volumes.get(@volume_id).wait_for { ready? }
|
||||
tests("#attach_volume(#{@server.id}, #{@volume_id}, '/dev/sdg')").formats(@volume_attach_format) do
|
||||
HP[:block_storage].compute.attach_volume(@server.id, @volume_id, "/dev/sdg").body['volumeAttachment']
|
||||
Fog::Compute[:hp].attach_volume(@server.id, @volume_id, "/dev/sdg").body['volumeAttachment']
|
||||
end
|
||||
|
||||
HP[:block_storage].volumes.get(@volume_id).wait_for { in_use? } unless Fog.mocking?
|
||||
tests("#detach_volume(#{@server.id}, #{@volume_id})").succeeds do
|
||||
HP[:block_storage].compute.detach_volume(@server.id, @volume_id)
|
||||
Fog::Compute[:hp].detach_volume(@server.id, @volume_id)
|
||||
end
|
||||
|
||||
HP[:block_storage].volumes.get(@volume_id).wait_for { ready? }
|
||||
|
@ -68,19 +68,19 @@ Shindo.tests("HP::BlockStorage | volume requests", ['hp', 'block_storage', 'volu
|
|||
end
|
||||
|
||||
tests("#attach_volume(0, 0, '/dev/sdg')").raises(Fog::Compute::HP::NotFound) do
|
||||
HP[:block_storage].compute.attach_volume(0, 0, "/dev/sdg")
|
||||
Fog::Compute[:hp].attach_volume(0, 0, "/dev/sdg")
|
||||
end
|
||||
tests("#attach_volume(#{@server.id}, 0, '/dev/sdg')").raises(Fog::Compute::HP::NotFound) do
|
||||
tests("#attach_volume(#{@server.id}, 0, '/dev/sdg')").raises(Fog::HP::BlockStorage::NotFound) do
|
||||
pending if Fog.mocking?
|
||||
HP[:block_storage].compute.attach_volume(@server.id, 0, "/dev/sdg")
|
||||
Fog::Compute[:hp].attach_volume(@server.id, 0, "/dev/sdg")
|
||||
end
|
||||
|
||||
tests("#detach_volume(0, 0)").raises(Fog::Compute::HP::NotFound) do
|
||||
HP[:block_storage].compute.detach_volume(0, 0)
|
||||
Fog::Compute[:hp].detach_volume(0, 0)
|
||||
end
|
||||
tests("#detach_volume(#{@server.id}, 0)").raises(Fog::Compute::HP::NotFound) do
|
||||
tests("#detach_volume(#{@server.id}, 0)").raises(Fog::HP::BlockStorage::NotFound) do
|
||||
pending if Fog.mocking?
|
||||
HP[:block_storage].compute.detach_volume(@server.id, 0)
|
||||
Fog::Compute[:hp].detach_volume(@server.id, 0)
|
||||
end
|
||||
|
||||
tests("#delete_volume(0)").raises(Fog::HP::BlockStorage::NotFound) do
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Shindo.tests('Fog::Compute[:hp] | metadata requests', ['hp']) do
|
||||
Shindo.tests("Fog::Compute[:hp] | metadata requests", ['hp']) do
|
||||
|
||||
@metadata_format = {
|
||||
'metadata' => Fog::Nullable::Hash
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Shindo.tests('Fog::Compute[:hp] | persistent server requests', ['hp', 'compute']) do
|
||||
Shindo.tests("Fog::Compute[:hp] | persistent server requests", ['hp', 'compute']) do
|
||||
|
||||
@server_format = {
|
||||
'addresses' => Fog::Nullable::Hash,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Shindo.tests('Fog::Compute[:hp] | volume requests', ['hp', 'compute', 'volumes']) do
|
||||
Shindo.tests("Fog::Compute[:hp] | volume requests", ['hp', 'compute', 'volumes']) do
|
||||
|
||||
@list_volume_attachments_format = {
|
||||
'volumeAttachments' => [{
|
||||
|
|
Loading…
Reference in New Issue