mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-22 18:50:08 +03:00
Merge branch 'feature-200' of dsa-research.org:one into feature-200
This commit is contained in:
commit
ed48b53497
@ -19,6 +19,7 @@
|
||||
|
||||
#include "PoolSQL.h"
|
||||
#include "ImageTemplate.h"
|
||||
#include "NebulaLog.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -367,6 +368,14 @@ private:
|
||||
db->exec(oss_templ);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* "Encrypts" the password with SHA1 digest
|
||||
* @param password
|
||||
* @return sha1 encrypted password
|
||||
*/
|
||||
string sha1_digest(const string& pass);
|
||||
|
||||
protected:
|
||||
|
||||
// *************************************************************************
|
||||
|
@ -194,26 +194,47 @@ public:
|
||||
return rc;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* This map stores the association between IIDs and Image names
|
||||
*/
|
||||
map<string, int> image_names;
|
||||
static const string& source_prefix()
|
||||
{
|
||||
return _source_prefix;
|
||||
};
|
||||
|
||||
static const string& default_type()
|
||||
{
|
||||
return _default_type;
|
||||
};
|
||||
|
||||
static const string& default_dev_prefix()
|
||||
{
|
||||
return _default_dev_prefix;
|
||||
};
|
||||
|
||||
private:
|
||||
//--------------------------------------------------------------------------
|
||||
// Configuration Attributes for Images
|
||||
// -------------------------------------------------------------------------
|
||||
/**
|
||||
* Path to the image repository
|
||||
**/
|
||||
string source_prefix;
|
||||
static string _source_prefix;
|
||||
|
||||
/**
|
||||
* Default image type
|
||||
**/
|
||||
string default_type;
|
||||
static string _default_type;
|
||||
|
||||
/**
|
||||
* Default device prefix
|
||||
**/
|
||||
string default_dev_prefix;
|
||||
static string _default_dev_prefix;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Pool Attributes
|
||||
// -------------------------------------------------------------------------
|
||||
/**
|
||||
* This map stores the association between IIDs and Image names
|
||||
*/
|
||||
map<string, int> image_names;
|
||||
|
||||
/**
|
||||
* Factory method to produce Image objects
|
||||
@ -243,12 +264,6 @@ private:
|
||||
*/
|
||||
int init_cb(void *nil, int num, char **values, char **names);
|
||||
|
||||
/**
|
||||
* "Encrypts" the password with SHA1 digest
|
||||
* @param password
|
||||
* @return sha1 encrypted password
|
||||
*/
|
||||
string sha1_digest(const string& pass);
|
||||
};
|
||||
|
||||
#endif /*IMAGE_POOL_H_*/
|
||||
|
83
share/hooks/image.rb
Executable file
83
share/hooks/image.rb
Executable file
@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2002-2010, OpenNebula Project Leads (OpenNebula.org) #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
|
||||
# not use this file except in compliance with the License. You may obtain #
|
||||
# a copy of the License at #
|
||||
# #
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 #
|
||||
# #
|
||||
# Unless required by applicable law or agreed to in writing, software #
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, #
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
|
||||
# See the License for the specific language governing permissions and #
|
||||
# limitations under the License. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
ONE_LOCATION=ENV["ONE_LOCATION"]
|
||||
|
||||
if !ONE_LOCATION
|
||||
RUBY_LIB_LOCATION="/usr/lib/one/ruby"
|
||||
VMDIR="/var/lib/one"
|
||||
else
|
||||
RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
|
||||
VMDIR=ONE_LOCATION+"/var"
|
||||
end
|
||||
|
||||
$: << RUBY_LIB_LOCATION
|
||||
|
||||
|
||||
require 'OpenNebula'
|
||||
require 'client_utilities'
|
||||
require 'pp'
|
||||
|
||||
if !(vm_id=ARGV[0])
|
||||
exit -1
|
||||
end
|
||||
|
||||
vm=OpenNebula::VirtualMachine.new_with_id(vm_id, get_one_client)
|
||||
vm.info
|
||||
template=vm.to_hash
|
||||
template=template['VM']['TEMPLATE']
|
||||
disks=[template['DISK']].flatten if template['DISK']
|
||||
disks.each_with_index do |disk,i|
|
||||
source_path=VMDIR+"/#{vm_id}/disk.#{i}"
|
||||
if disk["NAME"]# and File.exists?(source_path)
|
||||
if nil#disk["SAVE_AS"]
|
||||
# Perform the allocate if all goes well
|
||||
image=OpenNebula::Image.new(
|
||||
OpenNebula::Image.build_xml, get_one_client)
|
||||
begin
|
||||
template="NAME=#{disk['SAVE_AS']}\n"
|
||||
template+="TYPE=#{disk['TYPE'].upcase}\n" if DISK["TYPE"]
|
||||
result=image.allocate(template)
|
||||
rescue
|
||||
result=OpenNebula::Error.new("Error in template")
|
||||
end
|
||||
|
||||
# Get the allocated image
|
||||
image=OpenNebula::Image.new_with_id(image.id, get_one_client)
|
||||
image.info
|
||||
template=image.to_hash
|
||||
template=template['IMAGE']['TEMPLATE']
|
||||
|
||||
if !is_successful?(result)
|
||||
exit -1
|
||||
end
|
||||
elsif disk["OVERWRITE"]
|
||||
# Get the allocated image
|
||||
image=OpenNebula::Image.new_with_id(disk['IID'], get_one_client)
|
||||
image.info
|
||||
image.disable
|
||||
end
|
||||
# Perform the copy to the image repo if needed
|
||||
if File.copy(source_path, image['SOURCE'])
|
||||
result=image.enable
|
||||
else
|
||||
result=OpenNebula::Error.new(
|
||||
"Cannot copy image, please update before enabling it.")
|
||||
end
|
||||
end
|
||||
end
|
@ -19,6 +19,8 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <openssl/evp.h>
|
||||
#include <iomanip>
|
||||
|
||||
#include "Image.h"
|
||||
#include "ImagePool.h"
|
||||
@ -137,6 +139,76 @@ int Image::insert(SqlDB *db)
|
||||
{
|
||||
int rc;
|
||||
|
||||
string source_att;
|
||||
string type_att;
|
||||
string public_attr;
|
||||
string dev_prefix;
|
||||
|
||||
ostringstream tmp_hashstream;
|
||||
ostringstream tmp_sourcestream;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Check default image attributes
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// ------------ NAME --------------------
|
||||
|
||||
get_template_attribute("NAME", name);
|
||||
|
||||
if ( name.empty() == true )
|
||||
{
|
||||
goto error_name;
|
||||
}
|
||||
|
||||
// ------------ TYPE --------------------
|
||||
|
||||
get_template_attribute("TYPE", type_att);
|
||||
|
||||
transform (type_att.begin(), type_att.end(), type_att.begin(),
|
||||
(int(*)(int))toupper);
|
||||
|
||||
if ( type_att.empty() == true )
|
||||
{
|
||||
type_att = ImagePool::default_type();
|
||||
}
|
||||
|
||||
if (set_type(type_att) != 0)
|
||||
{
|
||||
goto error_type;
|
||||
}
|
||||
|
||||
// ------------ PUBLIC --------------------
|
||||
|
||||
get_template_attribute("PUBLIC", public_attr);
|
||||
|
||||
transform (public_attr.begin(), public_attr.end(), public_attr.begin(),
|
||||
(int(*)(int))toupper);
|
||||
|
||||
public_img = (public_attr == "YES");
|
||||
|
||||
// ------------ PREFIX --------------------
|
||||
|
||||
get_template_attribute("DEV_PREFIX", dev_prefix);
|
||||
|
||||
if( dev_prefix.empty() )
|
||||
{
|
||||
SingleAttribute * dev_att = new SingleAttribute("DEV_PREFIX",
|
||||
ImagePool::default_dev_prefix());
|
||||
|
||||
image_template.set(dev_att);
|
||||
}
|
||||
|
||||
// ------------ SOURCE (path to store the image)--------------------
|
||||
|
||||
tmp_hashstream << uid << ":" << name;
|
||||
|
||||
tmp_sourcestream << ImagePool::source_prefix() << "/";
|
||||
tmp_sourcestream << sha1_digest(tmp_hashstream.str());
|
||||
|
||||
source = tmp_sourcestream.str();
|
||||
|
||||
|
||||
// Set up the template ID, to insert it
|
||||
if ( image_template.id == -1 )
|
||||
{
|
||||
@ -164,6 +236,15 @@ int Image::insert(SqlDB *db)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_name:
|
||||
NebulaLog::log("IMG", Log::ERROR, "NAME not present in image template");
|
||||
goto error_common;
|
||||
error_type:
|
||||
NebulaLog::log("IMG", Log::ERROR, "Incorrect TYPE in image template");
|
||||
goto error_common;
|
||||
error_common:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
@ -583,6 +664,33 @@ int Image::disk_attribute(VectorAttribute * disk, int * index)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
string Image::sha1_digest(const string& pass)
|
||||
{
|
||||
EVP_MD_CTX mdctx;
|
||||
unsigned char md_value[EVP_MAX_MD_SIZE];
|
||||
unsigned int md_len;
|
||||
ostringstream oss;
|
||||
|
||||
EVP_MD_CTX_init(&mdctx);
|
||||
EVP_DigestInit_ex(&mdctx, EVP_sha1(), NULL);
|
||||
|
||||
EVP_DigestUpdate(&mdctx, pass.c_str(), pass.length());
|
||||
|
||||
EVP_DigestFinal_ex(&mdctx,md_value,&md_len);
|
||||
EVP_MD_CTX_cleanup(&mdctx);
|
||||
|
||||
for(unsigned int i = 0; i<md_len; i++)
|
||||
{
|
||||
oss << setfill('0') << setw(2) << hex << nouppercase
|
||||
<< (unsigned short) md_value[i];
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
|
@ -19,11 +19,12 @@
|
||||
/* ************************************************************************** */
|
||||
|
||||
#include "ImagePool.h"
|
||||
#include <openssl/evp.h>
|
||||
#include <iomanip>
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
string ImagePool::_source_prefix;
|
||||
string ImagePool::_default_type;
|
||||
string ImagePool::_default_dev_prefix;
|
||||
|
||||
int ImagePool::init_cb(void *nil, int num, char **values, char **names)
|
||||
{
|
||||
@ -40,19 +41,22 @@ int ImagePool::init_cb(void *nil, int num, char **values, char **names)
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
ImagePool::ImagePool( SqlDB * db,
|
||||
const string& _source_prefix,
|
||||
const string& _default_type,
|
||||
const string& _default_dev_prefix):
|
||||
|
||||
PoolSQL(db,Image::table),
|
||||
source_prefix(_source_prefix),
|
||||
default_type(_default_type),
|
||||
default_dev_prefix(_default_dev_prefix)
|
||||
ImagePool::ImagePool( SqlDB * db,
|
||||
const string& __source_prefix,
|
||||
const string& __default_type,
|
||||
const string& __default_dev_prefix):
|
||||
|
||||
PoolSQL(db,Image::table)
|
||||
{
|
||||
ostringstream sql;
|
||||
int rc;
|
||||
|
||||
// Init static defaults
|
||||
_source_prefix = __source_prefix;
|
||||
_default_type = __default_type;
|
||||
_default_dev_prefix = __default_dev_prefix;
|
||||
|
||||
// Set default type
|
||||
if (_default_type != "OS" &&
|
||||
_default_type != "CDROM" &&
|
||||
@ -60,7 +64,7 @@ ImagePool::ImagePool( SqlDB * db,
|
||||
{
|
||||
NebulaLog::log("IMG", Log::ERROR,
|
||||
"Bad default for image type, setting OS");
|
||||
default_type = "OS";
|
||||
_default_type = "OS";
|
||||
}
|
||||
|
||||
// Read from the DB the existing images, and build the ID:Name map
|
||||
@ -87,18 +91,11 @@ int ImagePool::allocate (
|
||||
const string& stemplate,
|
||||
int * oid)
|
||||
{
|
||||
Image * img;
|
||||
string name;
|
||||
string source;
|
||||
string type;
|
||||
string public_attr;
|
||||
string dev_prefix;
|
||||
|
||||
char * error_msg;
|
||||
int rc;
|
||||
Image * img;
|
||||
|
||||
ostringstream tmp_hashstream;
|
||||
ostringstream tmp_sourcestream;
|
||||
string name;
|
||||
char * error_msg;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Build a new Image object
|
||||
@ -115,68 +112,8 @@ int ImagePool::allocate (
|
||||
goto error_parse;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Check default image attributes
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
// ------------ NAME --------------------
|
||||
|
||||
img->get_template_attribute("NAME", name);
|
||||
|
||||
if ( name.empty() == true )
|
||||
{
|
||||
goto error_name;
|
||||
}
|
||||
|
||||
img->name = name;
|
||||
|
||||
// ------------ TYPE --------------------
|
||||
|
||||
img->get_template_attribute("TYPE", type);
|
||||
|
||||
transform (type.begin(), type.end(), type.begin(),
|
||||
(int(*)(int))toupper);
|
||||
|
||||
if ( type.empty() == true )
|
||||
{
|
||||
type = default_type;
|
||||
}
|
||||
|
||||
if (img->set_type(type) != 0)
|
||||
{
|
||||
goto error_type;
|
||||
}
|
||||
|
||||
// ------------ PUBLIC --------------------
|
||||
|
||||
img->get_template_attribute("PUBLIC", public_attr);
|
||||
|
||||
transform (public_attr.begin(), public_attr.end(), public_attr.begin(),
|
||||
(int(*)(int))toupper);
|
||||
|
||||
img->public_img = (public_attr == "YES");
|
||||
|
||||
// ------------ PREFIX --------------------
|
||||
|
||||
img->get_template_attribute("DEV_PREFIX", dev_prefix);
|
||||
|
||||
if( dev_prefix.empty() )
|
||||
{
|
||||
SingleAttribute * dev_att =
|
||||
new SingleAttribute("DEV_PREFIX", default_dev_prefix);
|
||||
|
||||
img->image_template.set(dev_att);
|
||||
}
|
||||
|
||||
// ------------ SOURCE (path to store the image)--------------------
|
||||
|
||||
tmp_hashstream << uid << ":" << name;
|
||||
|
||||
tmp_sourcestream << source_prefix << "/";
|
||||
tmp_sourcestream << sha1_digest(tmp_hashstream.str());
|
||||
|
||||
img->source = tmp_sourcestream.str();
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Insert the Object in the pool
|
||||
// ---------------------------------------------------------------------
|
||||
@ -196,17 +133,6 @@ int ImagePool::allocate (
|
||||
|
||||
return *oid;
|
||||
|
||||
error_name:
|
||||
NebulaLog::log("IMG", Log::ERROR, "NAME not present in image template");
|
||||
goto error_common;
|
||||
error_type:
|
||||
NebulaLog::log("IMG", Log::ERROR, "Incorrect TYPE in image template");
|
||||
goto error_common;
|
||||
error_common:
|
||||
delete img;
|
||||
*oid = -1;
|
||||
return -1;
|
||||
|
||||
error_parse:
|
||||
ostringstream oss;
|
||||
oss << "ImagePool template parse error: " << error_msg;
|
||||
@ -263,30 +189,3 @@ int ImagePool::dump(ostringstream& oss, const string& where)
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
string ImagePool::sha1_digest(const string& pass)
|
||||
{
|
||||
EVP_MD_CTX mdctx;
|
||||
unsigned char md_value[EVP_MAX_MD_SIZE];
|
||||
unsigned int md_len;
|
||||
ostringstream oss;
|
||||
|
||||
EVP_MD_CTX_init(&mdctx);
|
||||
EVP_DigestInit_ex(&mdctx, EVP_sha1(), NULL);
|
||||
|
||||
EVP_DigestUpdate(&mdctx, pass.c_str(), pass.length());
|
||||
|
||||
EVP_DigestFinal_ex(&mdctx,md_value,&md_len);
|
||||
EVP_MD_CTX_cleanup(&mdctx);
|
||||
|
||||
for(unsigned int i = 0; i<md_len; i++)
|
||||
{
|
||||
oss << setfill('0') << setw(2) << hex << nouppercase
|
||||
<< (unsigned short) md_value[i];
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
@ -85,6 +85,7 @@ class ImagePoolTest : public PoolTest
|
||||
CPPUNIT_TEST ( target_generation );
|
||||
CPPUNIT_TEST ( bus_source_assignment );
|
||||
CPPUNIT_TEST ( public_attribute );
|
||||
CPPUNIT_TEST ( disk_overwrite );
|
||||
CPPUNIT_TEST ( dump );
|
||||
CPPUNIT_TEST ( dump_where );
|
||||
|
||||
@ -156,6 +157,7 @@ protected:
|
||||
delete user_pool;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
ImagePoolTest(){};
|
||||
|
||||
@ -292,6 +294,8 @@ public:
|
||||
check(1, obj);
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
void wrong_get_name()
|
||||
{
|
||||
@ -548,6 +552,136 @@ public:
|
||||
delete disk;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
void disk_overwrite()
|
||||
{
|
||||
ImagePool * imp = static_cast<ImagePool *>(pool);
|
||||
Image * img;
|
||||
|
||||
VectorAttribute * disk;
|
||||
int oid, rc;
|
||||
string value;
|
||||
int index = 0;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Allocate an OS type image
|
||||
oid = allocate(0);
|
||||
CPPUNIT_ASSERT( oid > -1 );
|
||||
img = imp->get(oid, false);
|
||||
|
||||
// Disk with overwrite=yes, save_as empty
|
||||
disk = new VectorAttribute("DISK");
|
||||
|
||||
disk->replace("OVERWRITE", "yes");
|
||||
|
||||
img->enable(true);
|
||||
rc = img->disk_attribute(disk, &index);
|
||||
CPPUNIT_ASSERT( rc == 0 );
|
||||
|
||||
|
||||
value = disk->vector_value("OVERWRITE");
|
||||
CPPUNIT_ASSERT( value == "YES" );
|
||||
|
||||
value = "";
|
||||
value = disk->vector_value("SAVE_AS");
|
||||
CPPUNIT_ASSERT( value == "" );
|
||||
|
||||
value = "";
|
||||
value = disk->vector_value("CLONE");
|
||||
CPPUNIT_ASSERT( value == "NO" );
|
||||
|
||||
value = "";
|
||||
value = disk->vector_value("SAVE");
|
||||
CPPUNIT_ASSERT( value == "YES" );
|
||||
|
||||
value = "";
|
||||
value = disk->vector_value("READONLY");
|
||||
CPPUNIT_ASSERT( value == "NO" );
|
||||
|
||||
// clean up
|
||||
delete disk;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Allocate an OS type image
|
||||
oid = allocate(1);
|
||||
CPPUNIT_ASSERT( oid > -1 );
|
||||
img = imp->get(oid, false);
|
||||
|
||||
// Disk with overwrite=no, save_as not empty
|
||||
disk = new VectorAttribute("DISK");
|
||||
|
||||
disk->replace("OVERWRITE", "NO");
|
||||
disk->replace("SAVE_AS", "path_to_save");
|
||||
|
||||
img->enable(true);
|
||||
rc = img->disk_attribute(disk, &index);
|
||||
CPPUNIT_ASSERT( rc == 0 );
|
||||
|
||||
value = "";
|
||||
value = disk->vector_value("OVERWRITE");
|
||||
CPPUNIT_ASSERT( value == "NO" );
|
||||
|
||||
value = "";
|
||||
value = disk->vector_value("SAVE_AS");
|
||||
CPPUNIT_ASSERT( value == "path_to_save" );
|
||||
|
||||
value = "";
|
||||
value = disk->vector_value("CLONE");
|
||||
CPPUNIT_ASSERT( value == "YES" );
|
||||
|
||||
value = "";
|
||||
value = disk->vector_value("SAVE");
|
||||
CPPUNIT_ASSERT( value == "YES" );
|
||||
|
||||
value = "";
|
||||
value = disk->vector_value("READONLY");
|
||||
CPPUNIT_ASSERT( value == "NO" );
|
||||
|
||||
// clean up
|
||||
delete disk;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Allocate an OS type image
|
||||
oid = allocate(2);
|
||||
CPPUNIT_ASSERT( oid > -1 );
|
||||
img = imp->get(oid, false);
|
||||
|
||||
// Disk with overwrite=no, save_as not present
|
||||
disk = new VectorAttribute("DISK");
|
||||
|
||||
disk->replace("OVERWRITE", "NO");
|
||||
|
||||
img->enable(true);
|
||||
rc = img->disk_attribute(disk, &index);
|
||||
CPPUNIT_ASSERT( rc == 0 );
|
||||
|
||||
value = "";
|
||||
value = disk->vector_value("OVERWRITE");
|
||||
CPPUNIT_ASSERT( value == "NO" );
|
||||
|
||||
value = "";
|
||||
value = disk->vector_value("SAVE_AS");
|
||||
CPPUNIT_ASSERT( value == "" );
|
||||
|
||||
value = "";
|
||||
value = disk->vector_value("CLONE");
|
||||
CPPUNIT_ASSERT( value == "YES" );
|
||||
|
||||
value = "";
|
||||
value = disk->vector_value("SAVE");
|
||||
CPPUNIT_ASSERT( value == "NO" );
|
||||
|
||||
value = "";
|
||||
value = disk->vector_value("READONLY");
|
||||
CPPUNIT_ASSERT( value == "NO" );
|
||||
|
||||
// clean up
|
||||
delete disk;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user