Revision ade6513a

View differences:

include/ImageManager.h
115 115

  
116 116
    /**
117 117
     *  Closes any cloning operation on the image, updating the state if needed
118
     *    @param iid image id of the image to be released
119
     *    @param clone_img_id image id of the image that was being cloned
118
     *    @param iid image id of the image to that was being cloned
119
     *    @param clone_img_id the cloned image (id > 0) or market app (id =< 0)
120 120
     */
121 121
    void release_cloning_image(int iid, int clone_img_id);
122 122

  
......
147 147
     *
148 148
     * @return 0 if the image can be cloned, -1 otherwise
149 149
     */
150
    int can_clone_image(int             cloning_id,
151
                        ostringstream&  oss_error);
150
    int can_clone_image(int cloning_id, ostringstream&  oss_error);
151

  
152
    /**
153
     * Sets the state to CLONE for the given image
154
     *   @param new_id for the target image (new_id>0) or market app (new_id =<0)
155
     *   @param clonning_id the ID of the image to be cloned
156
     *   @param error if any
157
     *   @return 0 if siccess
158
     */
159
    int set_clone_state(int new_id, int cloning_id, std::string& error);
152 160

  
153 161
    /**
154 162
     *  Clone an existing image to the repository
......
162 170
                    int cloning_id,
163 171
                    const string& ds_data,
164 172
                    string& error);
165

  
166 173
    /**
167 174
     *  Deletes an image from the repository and the DB. The Datastore image list
168 175
     *  is also updated
include/MarketPlaceApp.h
139 139
        return type;
140 140
    };
141 141

  
142
    /**
143
     * Returns the ID of the object originating this app
144
     *    @return the image, vmtemplate or flow id
145
     */
146
    int get_origin_id() const
147
    {
148
        return origin_id;
149
    };
150

  
142 151
    //--------------------------------------------------------------------------
143 152
    // Set Marketplace app attributes
144 153
    //--------------------------------------------------------------------------
......
232 241
    /**
233 242
     *  Origin of this App
234 243
     */
235
    std::string origin;
244
    int origin_id;
236 245

  
237 246
    // *************************************************************************
238 247
    // Constructor
include/MarketPlaceManager.h
25 25

  
26 26
class MarketPlacePool;
27 27
class MarketPlaceAppPool;
28
class ImagePool;
29
class DatastorePool;
30

  
31
class ImageManager;
28 32

  
29 33
class MarketPlaceManager : public MadManager, public ActionListener
30 34
{
......
36 40
     *    @param m, monitor_period to monitor marketplaces
37 41
     *    @param mad, list of drivers for the manager
38 42
     */
39
    MarketPlaceManager( time_t t, time_t m, std::vector<const Attribute*>& mad);
43
    MarketPlaceManager(time_t t, time_t m, std::vector<const Attribute*>& mad);
40 44

  
41 45
    ~MarketPlaceManager(){};
42 46

  
43 47
    /**
48
     * Initializes internal pointers to other managers. Must be called when
49
     * all the other managers exist in Nebula::instance
50
     */
51
    void init_managers();
52

  
53
    /**
44 54
     *  This functions starts the associated listener thread, and creates a
45 55
     *  new thread for the MarketPlace Manager. This thread will wait in
46 56
     *  an action loop till it receives ACTION_FINALIZE.
......
75 85

  
76 86
    /**
77 87
     *  Imports a new app into the marketplace. The marketplace app needs to
78
     *  include the ORIGIN attribute so the driver can locate the app. An
88
     *  include the ORIGIN_ID attribute so the driver can locate the app. An
79 89
     *  optional template maybe provided to export the app to a cloud.
80 90
     *    @param appid of the app
81 91
     *    @param market_data of the associated marketplace in XML format
......
142 152
     */
143 153
    MarketPlaceAppPool * apppool;
144 154

  
155
	/**
156
     *  Pointer to the image pool
157
     */
158
	ImagePool *          ipool;
159

  
160
	/**
161
     * Pointer to the image pool
162
     */
163
	DatastorePool *      dspool;
164

  
165
	/**
166
	 *  Pointer to the Image Manger
167
     */
168
	ImageManager *       imagem;
169

  
145 170
    /**
146 171
     *  Action engine for the Manager
147 172
     */
install.sh
1207 1207
#-------------------------------------------------------------------------------
1208 1208
# Marketplace drivers, to be installed under $REMOTES_LOCATION/market
1209 1209
#   - HTTP based marketplace, $REMOTES_LOCATION/market/http
1210
#   - S3-obeject based Image Repository, $REMOTES_LOCATION/datastore/fs
1211
#   - VMFS based Image Repository, $REMOTES_LOCATION/datastore/vmfs
1212
#   - LVM based Image Repository, $REMOTES_LOCATION/datastore/lvm
1210
#   - S3-obeject based marketplace, $REMOTES_LOCATION/market/s3
1213 1211
#-------------------------------------------------------------------------------
1214 1212

  
1215 1213
MARKETPLACE_DRIVER_HTTP_SCRIPTS="src/market_mad/remotes/http/import \
src/cli/one_helper/onemarketapp_helper.rb
129 129
        puts str % ["VERSION", app['VERSION']]
130 130
        puts str % ["DESCRIPTION", app['DESCRIPTION']]
131 131
        puts str % ["SIZE", OpenNebulaHelper.unit_to_str(app['SIZE'].to_i,{},'M')]
132
        puts str % ["ORIGIN_ID", app['ORIGIN_ID']]
133
        puts str % ["FORMAT", app['FORMAT']]
132 134

  
133 135
        puts
134 136

  
src/cloud/marketplace/bin/onemarket
1
#!/usr/bin/env ruby
2

  
3
# ---------------------------------------------------------------------------- #
4
# Copyright 2002-2015, OpenNebula Project, OpenNebula Systems                  #
5
#                                                                              #
6
# Licensed under the Apache License, Version 2.0 (the "License"); you may      #
7
# not use this file except in compliance with the License. You may obtain      #
8
# a copy of the License at                                                     #
9
#                                                                              #
10
# http://www.apache.org/licenses/LICENSE-2.0                                   #
11
#                                                                              #
12
# Unless required by applicable law or agreed to in writing, software          #
13
# distributed under the License is distributed on an "AS IS" BASIS,            #
14
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.     #
15
# See the License for the specific language governing permissions and          #
16
# limitations under the License.                                               #
17
# ---------------------------------------------------------------------------- #
18

  
19
ONE_LOCATION=ENV["ONE_LOCATION"]
20

  
21
if !ONE_LOCATION
22
    RUBY_LIB_LOCATION="/usr/lib/one/ruby"
23
else
24
    RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
25
    TEMPLATES_LOCATION=ONE_LOCATION+"/etc/occi_templates"
26
    CONF_LOCATION=ONE_LOCATION+"/etc"
27
end
28

  
29
$: << RUBY_LIB_LOCATION
30
$: << RUBY_LIB_LOCATION+"/cloud"
31

  
32
require 'marketplace/marketplace_client'
33

  
34
require 'cli/command_parser'
35
require 'cli/cli_helper'
36

  
37
require 'rubygems'
38
require 'json'
39

  
40
USER_AGENT = "CLI"
41

  
42
#
43
# Options
44
#
45

  
46
DEFAULT_OPTIONS = [
47
    ENDPOINT = {
48
        :name => "server",
49
        :short => "-s url",
50
        :large => "--server url",
51
        :format => String,
52
        :description => "Marketplace endpoint"
53
    },
54
    USERNAME={
55
        :name => "username",
56
        :short => "-u name",
57
        :large => "--username name",
58
        :format => String,
59
        :description => "User name"
60
    },
61
    PASSWORD={
62
        :name => "password",
63
        :short => "-p pass",
64
        :large => "--password pass",
65
        :format => String,
66
        :description => "User password"
67
    }
68
]
69

  
70
JSON_FORMAT={
71
    :name => "json",
72
    :short => "-j",
73
    :large => "--json",
74
    :description => "Show in JSON format"
75
}
76

  
77
#
78
# Table
79
#
80

  
81
TABLE = CLIHelper::ShowTable.new(nil, self) do
82
    column :ID, "Appliance", :size=>25 do |d|
83
        d["_id"]["$oid"]
84
    end
85

  
86
    column :NAME, "Name", :size=>50 do |d|
87
        d["name"]
88
    end
89

  
90
    column :PUBLISHER, "Publisher", :size=>15 do |d|
91
        d["publisher"]
92
    end
93

  
94
    default :ID, :NAME, :PUBLISHER
95
end
96

  
97
#
98
# Commands
99
#
100

  
101
cmd=CommandParser::CmdParser.new(ARGV) do
102
    usage "`onemarket` <command> [<args>] [<options>]"
103

  
104
    set :option, DEFAULT_OPTIONS
105

  
106
    #
107
    # List
108
    #
109

  
110
    list_desc = <<-EOT.unindent
111
        List the available appliances in the Marketplace
112
    EOT
113

  
114
    command :list, list_desc, :options => JSON_FORMAT do
115
        client = Market::ApplianceClient.new(
116
                    options[:username],
117
                    options[:password],
118
                    options[:server],
119
                    USER_AGENT)
120

  
121
        response = client.list
122

  
123
        if CloudClient::is_error?(response)
124
            [response.code.to_i, response.to_s]
125
        else
126
            if options[:json]
127
                [0,response.body]
128
            else
129
                array_list = JSON.parse(response.body)
130
                TABLE.show(array_list['appliances'])
131
                0
132
            end
133
        end
134
    end
135

  
136
    #
137
    # Create
138
    #
139

  
140
    create_desc = <<-EOT.unindent
141
        Create a new appliance in the Marketplace
142
    EOT
143

  
144
    command :create, create_desc, :file do
145
        client = Market::ApplianceClient.new(
146
                    options[:username],
147
                    options[:password],
148
                    options[:server],
149
                    USER_AGENT)
150

  
151
        response = client.create(File.read(args[0]))
152

  
153
        if CloudClient::is_error?(response)
154
            [response.code.to_i, response.to_s]
155
        else
156
            [0, response.body]
157
        end
158
    end
159

  
160
    #
161
    # Show
162
    #
163

  
164
    show_desc = <<-EOT.unindent
165
        Show detailed information of a given appliance
166
    EOT
167

  
168
    command :show, show_desc, :id, :options => JSON_FORMAT do
169
        client = Market::ApplianceClient.new(
170
                    options[:username],
171
                    options[:password],
172
                    options[:server],
173
                    USER_AGENT)
174

  
175
        response = client.show(args[0])
176

  
177
        if CloudClient::is_error?(response)
178
            [response.code.to_i, response.to_s]
179
        else
180
            [0,response.body]
181
        end
182
    end
183
end
src/cloud/marketplace/lib/marketplace_client.rb
1
# ---------------------------------------------------------------------------- #
2
# Copyright 2002-2015, OpenNebula Project, OpenNebula Systems                  #
3
#                                                                              #
4
# Licensed under the Apache License, Version 2.0 (the "License"); you may      #
5
# not use this file except in compliance with the License. You may obtain      #
6
# a copy of the License at                                                     #
7
#                                                                              #
8
# http://www.apache.org/licenses/LICENSE-2.0                                   #
9
#                                                                              #
10
# Unless required by applicable law or agreed to in writing, software          #
11
# distributed under the License is distributed on an "AS IS" BASIS,            #
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.     #
13
# See the License for the specific language governing permissions and          #
14
# limitations under the License.                                               #
15
# ---------------------------------------------------------------------------- #
16

  
17
require 'uri'
18
require 'cloud/CloudClient'
19

  
20
require 'ostruct'
21

  
22
module Market
23
    class Client
24
        def initialize(username, password, url, user_agent="Ruby")
25
            @username = username
26
            @password = password
27

  
28
            url ||= 'http://marketplace.opennebula.systems/'
29
            @uri = URI.parse(url)
30

  
31
            @user_agent = "OpenNebula #{CloudClient::VERSION} (#{user_agent})"
32

  
33
            @host = nil
34
            @port = nil
35

  
36
            if ENV['http_proxy']
37
                uri_proxy  = URI.parse(ENV['http_proxy'])
38
                @host = uri_proxy.host
39
                @port = uri_proxy.port
40
            end
41
        end
42

  
43
        def get(path)
44
            req = Net::HTTP::Proxy(@host, @port)::Get.new(@uri.path + path)
45
            do_request(req)
46
        end
47

  
48
        def post(path, body)
49
            req = Net::HTTP::Proxy(@host, @port)::Post.new(@uri.path + path)
50
            req.body = body
51

  
52
            do_request(req)
53
        end
54

  
55
        private
56

  
57
        def do_request(req)
58
            if @username && @password
59
                req.basic_auth @username, @password
60
            end
61

  
62
            req['User-Agent'] = @user_agent
63

  
64
            res = CloudClient::http_start(@uri, @timeout) do |http|
65
                http.request(req)
66
            end
67

  
68
            res
69
        end
70
    end
71

  
72

  
73
    class ApplianceClient < Client
74
        def initialize(user, password, url, agent)
75
            super(user, password, url, agent)
76
        end
77

  
78
        def list
79
            get("/appliance")
80
        end
81

  
82
        def create(body)
83
            post("/appliance", body)
84
        end
85

  
86
        def show(id)
87
            get("/appliance/#{id}")
88
        end
89
    end
90
end
src/datastore_mad/remotes/downloader.sh
176 176

  
177 177
    command="curl $curl_args"
178 178
    ;;
179
ssh://*)
180
    # pseudo-url for ssh transfers ssh://user@host:path
181
    # -l to limit the bw
182
    ssh_src=$(echo $FROM | grep -Po '(?<=ssh://).+')
183
    ssh_arg=(${ssh_src//:/ })
184

  
185
    rmt_cmd="'cat ${ssh_arg[1]}'"
186

  
187
    command="ssh ${ssh_arg[0]} $rmt_cmd"
188
    ;;
179 189
*)
180 190
    if [ ! -r $FROM ]; then
181 191
        echo "Cannot read from $FROM" >&2
src/datastore_mad/remotes/fs/export
45 45
while IFS= read -r -d '' element; do
46 46
    XPATH_ELEMENTS[i++]="$element"
47 47
done < <($XPATH     /DS_DRIVER_ACTION_DATA/IMAGE/SOURCE \
48
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/BRIDGE_LIST \
49
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/EXPORT_DIR \
50
                    /DS_DRIVER_ACTION_DATA/DATASTORE/BASE_PATH)
48
                    /DS_DRIVER_ACTION_DATA/DATASTORE/TEMPLATE/BRIDGE_LIST)
51 49
unset i
52 50

  
53 51
SRC="${XPATH_ELEMENTS[i++]}"
54 52
BRIDGE_LIST="${XPATH_ELEMENTS[i++]}"
55
EXPORT_DIR="${XPATH_ELEMENTS[i++]}"
56
BASE_PATH="${XPATH_ELEMENTS[i++]}"
57 53

  
58
EXPORT_DIR="${EXPORT_DIR-$BASE_PATH}"
59

  
60
# -------------------- Convert image to qcow2 format ---------------------------
61
# TODO:
62
#   Export directories needs to be defined, probably in oned.conf
63
#   BRIDGE_LIST needs to be handled
64 54
#-------------------------------------------------------------------------------
55
# Output image source and format
56
#-------------------------------------------------------------------------------
57

  
58
INFO_SCRIPT=$(cat <<EOF
59

  
60
CHECKSUM=\$(${MD5SUM} ${SRC} | cut -f1 -d' ')
61
SIZE=\$(${DU} -LBM ${SRC} | cut -f1)
62
FORMAT=\$(${QEMU_IMG} info ${SRC} 2>/dev/null | grep -Po '(?<=file format: )\w+')
63

  
64
echo "<CHECKSUM>\$CHECKSUM</CHECKSUM>"
65
echo "<SIZE>\$SIZE</SIZE>"
66
echo "<FORMAT>\$FORMAT</FORMAT>"
67

  
68
EOF
69
)
65 70

  
66
log "Removing $SRC from the image repository"
71
if [ -n "$BRIDGE_LIST" ]; then
72
    HOST=`get_destination_host $ID`
73
    INFO=$(ssh_monitor_and_log "$HOST" "$INFO_SCRIPT" "Image info script" 2>&1)
74
    SRC="ssh://$HOST:$SRC"
75
else
76
    INFO=$(monitor_and_log "$INFO_SCRIPT" "Image info script" 2>&1)
77
fi
78

  
79
INFO_STATUS=$?
80

  
81
if [ "$INFO_STATUS" != "0" ]; then
82
    echo "$INFO"
83
    exit $INFO_STATUS
84
fi
67 85

  
68
exec_and_log "$QEMU_IMG convert -O qcow2 $SRC $SRC.export" \
69
	"Error converting image to qcow2 format"
86
echo "<IMPORT_INFO><IMPORT_SOURCE>$SRC</IMPORT_SOURCE> \
87
      $INFO \
88
      <DISPOSE>NO</DISPOSE></IMPORT_INFO>"
70 89

  
71
echo "$SRC.export"
src/datastore_mad/remotes/libfs.sh
53 53
}
54 54

  
55 55
#-------------------------------------------------------------------------------
56
# Get file format using qemu-img
57
#   @return string representation of the format, empty if error
58
#-------------------------------------------------------------------------------
59
function image_format {
60
    echo "$($QEMU_IMG info $1 2>/dev/null | grep -Po '(?<=file format: )\w+')"
61
}
62

  
63
#-------------------------------------------------------------------------------
56 64
# Generates an unique image hash. Requires ID to be set
57 65
#   @return hash for the image (empty if error)
58 66
#-------------------------------------------------------------------------------
src/image/ImageManagerActions.cc
287 287
        case Image::ERROR:
288 288
        case Image::USED_PERS:
289 289
        case Image::LOCKED:
290
            ostringstream oss;
291

  
292
            oss << "Releasing image in wrong state: "
293
                << Image::state_to_str(img->get_state());
294

  
295
            NebulaLog::log("ImM", Log::ERROR, oss.str());
290
            NebulaLog::log("ImM", Log::ERROR, "Release cloning image"
291
                " in wrong state");
296 292
            break;
297 293
    }
298 294

  
......
564 560
/* -------------------------------------------------------------------------- */
565 561
/* -------------------------------------------------------------------------- */
566 562

  
567
int ImageManager::can_clone_image(  int             cloning_id,
568
                                    ostringstream&  oss_error)
563
int ImageManager::can_clone_image(int cloning_id, ostringstream&  oss_error)
569 564
{
570 565
    Image *       img;
571 566

  
......
607 602
/* -------------------------------------------------------------------------- */
608 603
/* -------------------------------------------------------------------------- */
609 604

  
610
int ImageManager::clone_image(int   new_id,
611
                              int   cloning_id,
612
                              const string& ds_data,
613
                              string& error)
605
int ImageManager::set_clone_state(int new_id, int cloning_id, std::string& error)
614 606
{
615
    const ImageManagerDriver* imd = get();
616

  
617
    ostringstream oss;
618
    Image *       img;
619

  
620
    string  path;
621
    string  img_tmpl;
622
    string* drv_msg;
623

  
624
    if ( imd == 0 )
625
    {
626
        error = "Could not get datastore driver";
627

  
628
        NebulaLog::log("ImM", Log::ERROR, error);
629
        return -1;
630
    }
631

  
632
    img = ipool->get(cloning_id, true);
607
    int     rc  = 0;
608
    Image * img = ipool->get(cloning_id, true);
633 609

  
634 610
    if (img == 0)
635 611
    {
......
652 628
            }
653 629

  
654 630
            ipool->update(img);
655

  
656
            img->unlock();
657
        break;
631
            break;
658 632

  
659 633
        case Image::USED:
660 634
        case Image::CLONE:
661 635
            img->inc_cloning(new_id);
662

  
663 636
            ipool->update(img);
664

  
665
            img->unlock();
666
        break;
637
            break;
667 638

  
668 639
        case Image::USED_PERS:
669 640
        case Image::INIT:
......
671 642
        case Image::ERROR:
672 643
        case Image::DELETE:
673 644
        case Image::LOCKED:
674
            oss << "Cannot clone image in state: "
675
                << Image::state_to_str(img->get_state());
645
            error = "Cannot clone image in current state";
646
            rc    = -1;
647
            break;
648
    }
676 649

  
677
            error = oss.str();
678
            img->unlock();
679
            return -1;
680
        break;
650
    img->unlock();
651

  
652
    return rc;
653
}
654

  
655
/* -------------------------------------------------------------------------- */
656
/* -------------------------------------------------------------------------- */
657

  
658
int ImageManager::clone_image(int   new_id,
659
                              int   cloning_id,
660
                              const string& ds_data,
661
                              string& error)
662
{
663
    const ImageManagerDriver* imd = get();
664

  
665
    ostringstream oss;
666
    Image *       img;
667

  
668
    string  path;
669
    string  img_tmpl;
670
    string* drv_msg;
671

  
672
    if ( imd == 0 )
673
    {
674
        error = "Could not get datastore driver";
675

  
676
        NebulaLog::log("ImM", Log::ERROR, error);
677
        return -1;
678
    }
679

  
680
    if ( set_clone_state(new_id, cloning_id, error) == -1 )
681
    {
682
        return -1;
681 683
    }
682 684

  
683 685
    img = ipool->get(new_id,true);
684 686

  
685
    if (img == 0) //TODO: Rollback cloning counter
687
    if (img == 0)
686 688
    {
689
        release_cloning_image(cloning_id, new_id);
690

  
687 691
        error = "Target image deleted during cloning operation";
688 692
        return -1;
689 693
    }
src/market/MarketPlaceApp.cc
99 99
    type = IMAGE;
100 100

  
101 101
    //Known attributes
102
    //ORIGIN
102
    //ORIGIN_ID
103 103
    //DESCRIPTION
104 104
    //APPTEMPLATE64
105 105
    //PUBLISHER
106 106
    //VERSION
107
    erase_template_attribute("ORIGIN", origin);
108

  
109
    if (origin.empty())
107
    if (!get_template_attribute("ORIGIN_ID", origin_id))
110 108
    {
111 109
        goto error_origin;
112 110
    }
113 111

  
112
    remove_template_attribute("ORIGIN_ID");
113

  
114 114
    get_template_attribute("DESCRIPTION", description);
115 115

  
116 116
    get_template_attribute("APPTEMPLATE64", apptemplate64);
......
136 136
    return insert_replace(db, false, error_str);
137 137

  
138 138
error_origin:
139
    error_str = "Missing ORIGIN for the MARKETPLACEAPP";
139
    error_str = "Missing ORIGIN_ID for the MARKETPLACEAPP";
140 140
    NebulaLog::log("MKP", Log::ERROR, error_str);
141 141
    return -1;
142 142
}
......
241 241
			"<GNAME>"          << gname         << "</GNAME>" <<
242 242
			"<DATE>"           << date          << "</DATE>" <<
243 243
			"<NAME>"           << name          << "</NAME>" <<
244
            "<ORIGIN>"         << origin        << "</ORIGIN>" <<
244
            "<ORIGIN_ID>"      << origin_id     << "</ORIGIN_ID>" <<
245 245
            "<SOURCE>"         << source        << "</SOURCE>" <<
246 246
            "<CHECKSUM>"       << checksum      << "</CHECKSUM>" <<
247 247
            "<SIZE>"           << size_mb       << "</SIZE>" <<
......
283 283
    rc += xpath(gname,        "/MARKETPLACEAPP/GNAME", "not_found");
284 284
    rc += xpath(name,         "/MARKETPLACEAPP/NAME", "not_found");
285 285
    rc += xpath(date,         "/MARKETPLACEAPP/DATE", -1);
286
    rc += xpath(source,       "/MARKETPLACEAPP/SOURCE","not_found");
287
    rc += xpath(origin,       "/MARKETPLACEAPP/ORIGIN","not_found");
286
    rc += xpath(source,       "/MARKETPLACEAPP/SOURCE", "not_found");
287
    rc += xpath(origin_id,    "/MARKETPLACEAPP/ORIGIN_ID", -1);
288 288
    rc += xpath(istate,       "/MARKETPLACEAPP/STATE", -1);
289 289
    rc += xpath(itype,        "/MARKETPLACEAPP/TYPE",  -1);
290 290
    rc += xpath(description,  "/MARKETPLACEAPP/DESCRIPTION", "not_found");
src/market/MarketPlaceManager.cc
55 55
            std::vector<const Attribute*>& _mads):
56 56
        MadManager(_mads),
57 57
        timer_period(_timer_period),
58
        monitor_period(_monitor_period)
58
        monitor_period(_monitor_period),
59
        imagem(0)
59 60
{
60 61
    Nebula& nd = Nebula::instance();
61 62

  
62 63
    mppool  = nd.get_marketpool();
63 64
    apppool = nd.get_apppool();
65
    dspool  = nd.get_dspool();
66
    ipool   = nd.get_ipool();
64 67

  
65 68
    am.addListener(this);
66 69
};
......
113 116
/* -------------------------------------------------------------------------- */
114 117
/* -------------------------------------------------------------------------- */
115 118

  
119
void MarketPlaceManager::init_managers()
120
{
121
    Nebula& nd = Nebula::instance();
122

  
123
    imagem = nd.get_imagem();
124
}
125

  
126
/* -------------------------------------------------------------------------- */
127
/* -------------------------------------------------------------------------- */
128

  
116 129
int MarketPlaceManager::start()
117 130
{
118 131
    int            rc;
src/market/MarketPlaceManagerActions.cc
19 19
#include "MarketPlaceAppPool.h"
20 20
#include "MarketPlaceManagerDriver.h"
21 21

  
22
#include "Image.h"
23
#include "Datastore.h"
24
#include "ImageManager.h"
25

  
22 26
#include "NebulaLog.h"
23 27
#include "Nebula.h"
24 28

  
......
31 35
        const std::string& market_data,
32 36
        std::string&       err)
33 37
{
34
    std::string      app_data;
38
    std::string app_data, image_data, ds_data;
39
	std::string * msg;
40

  
41
	Image * image;
42
	Datastore * ds;
43

  
44
	int ds_id;
45

  
46
    const MarketPlaceManagerDriver* mpmd = get();
47

  
48
    if ( mpmd == 0 )
49
    {
50
        err = "Error getting MarketPlaceManagerDriver";
51
        return -1;
52
    }
53

  
35 54
    MarketPlaceApp * app = apppool->get(appid, true);
36 55

  
37 56
    if ( app == 0 )
......
42 61

  
43 62
    app->to_xml(app_data);
44 63

  
45
    app->unlock();
64
    MarketPlaceApp::MarketPlaceAppType type = app->get_type();
46 65

  
47
    std::string * msg = format_message(app_data, market_data, "");
66
	int app_id    = app->get_oid();
67
    int origin_id = app->get_origin_id();
48 68

  
49
    const MarketPlaceManagerDriver* mpmd = get();
69
    app->unlock();
50 70

  
51
    if ( mpmd == 0 )
71
    switch (type)
52 72
    {
53
        err = "Error getting MarketPlaceManagerDriver";
54
        return -1;
73
        case MarketPlaceApp::IMAGE:
74
            image = ipool->get(origin_id, true);
75

  
76
			if ( image == 0 )
77
			{
78
				err = "Image does not exist.";
79
				return -1;
80
			}
81

  
82
            image->to_xml(image_data);
83

  
84
            ds_id = image->get_ds_id();
85

  
86
            image->unlock();
87

  
88
            ds = dspool->get(ds_id, true);
89

  
90
			if ( ds == 0 )
91
			{
92
				err = "Image datastore no longer exists.";
93
				return -1;
94
			}
95

  
96
            ds->to_xml(ds_data);
97

  
98
            ds->unlock();
99

  
100
			if (imagem->set_clone_state(-app_id, origin_id, err) != 0)
101
			{
102
				return -1;
103
			}
104
			break;
105

  
106
        case MarketPlaceApp::VMTEMPLATE:
107
        case MarketPlaceApp::FLOW:
108
        case MarketPlaceApp::UNKNOWN:
109
            err = "Marketplace app type not supported.";
110
            return -1;
55 111
    }
56 112

  
113
    msg = format_message(app_data, market_data, image_data + ds_data);
114

  
57 115
    mpmd->importapp(appid, *msg);
58 116

  
59 117
    delete msg;
src/market_mad/one_market.rb
93 93
    end
94 94

  
95 95
    ############################################################################
96
    # Image Manager Protocol Actions (generic implementation)
96
    # Import a marketplace app into the marketplace. This is a two step process:
97
    #   1- The associated datastore_mad/export script is invoked to generate
98
    #      a file representation of the app.
99
    #   2- The resulting file path is used to import it into the marketplace
100
    #      invoking marketplace_mad/import.
97 101
    ############################################################################
98

  
99 102
    def import(id, drv_message)
100 103
        xml = decode(drv_message)
101 104

  
......
105 108
        end
106 109

  
107 110
        type   = xml['MARKETPLACEAPP/TYPE']
108
        origin = xml['MARKETPLACEAPP/ORIGIN']
111
        origin = xml['MARKETPLACEAPP/ORIGIN_ID']
109 112
        mp_mad = xml['MARKETPLACE/MARKET_MAD']
110 113

  
111 114
        if type.nil? || origin.nil? || mp_mad.nil?
112
            failure(:import, id,"Wrong driver message format")
115
            failure(:import, id, "Wrong driver message format")
113 116
            return
114 117
        end
115 118

  
119
        case OpenNebula::MarketPlaceApp::MARKETPLACEAPP_TYPES[type.to_i]
116 120
        #-----------------------------------------------------------------------
117
        #  Export origin to a path
121
        # Export marketplace origin to a file path, IMAGE
118 122
        #-----------------------------------------------------------------------
119
        case OpenNebula::MarketPlaceApp::MARKETPLACEAPP_TYPES[type.to_i]
120
          when "IMAGE" then
121
            if ( origin =~ /\d+$/ )
122
                # Get the associated datastore ID
123
                image = OpenNebula::Image.new_with_id(origin, @one)
124
                rc    = image.info
125

  
126
                if OpenNebula.is_error?(rc)
127
                    failure(:import, id, "Cannot find information for image "\
128
                            "#{origin}: #{rc.to_str()}")
129
                    return
130
                end
131

  
132
                ds_id = image['DATASTORE_ID']
133

  
134
                if ds_id.nil?
135
                    failure(:import, id, "Cannot find datastore for image #{origin}")
136
                    return
137
                end
138

  
139
                ds = OpenNebula::Datastore.new_with_id(ds_id, @one)
140
                rc = ds.info
141

  
142
                if OpenNebula.is_error?(rc)
143
                    failure(:import, id, "Datastore #{ds_id} not found: #{rc}")
144
                    return
145
                end
146

  
147
                ds_mad = ds['DS_MAD']
148

  
149
                if ds_mad.nil?
150
                    failure(:import, id, "Cannot find datastore driver")
151
                    return
152
                end
153

  
154
                #Execute export action from Datastore
155
                ds_msg   = "<DS_DRIVER_ACTION_DATA>"\
156
                           "#{image.to_xml}"\
157
                           "#{ds.to_xml}"\
158
                           "</DS_DRIVER_ACTION_DATA>"
159
                ds_msg64 = Base64::strict_encode64(ds_msg)
160

  
161
                result, info = do_action(id, nil, ds_mad, :export,
162
                    "#{ds_msg64} #{id}", false)
163

  
164
                if ( result == RESULT[:failure] )
165
                    failure(:import, id, "Error exporting image to file: #{info}")
166
                    return
167
                end
168

  
169
                source = info
170
            elsif ( source =~ /\/.+|https?:\/\// )
171
                source = origin
172
            else
173
                failure(:import, id, "Origin is not a valid ID, path or URL")
123
        when "IMAGE" then
124
            # ------------ Execute export action from Datastore ----------------
125
            ds_mad = xml['DATASTORE/DS_MAD']
126

  
127
            if ds_mad.nil?
128
                failure(:import, id, "Wrong driver message format")
174 129
                return
175 130
            end
176
          else # Only IMAGE type is supported
177
                failure(:import, id, "Type #{apptype} not supported")
131

  
132
            ds_msg = "<DS_DRIVER_ACTION_DATA>"\
133
                     "#{xml.element_xml('IMAGE')}"\
134
                     "#{xml.element_xml('DATASTORE')}"\
135
                     "</DS_DRIVER_ACTION_DATA>"
136

  
137
            ds_msg64 = Base64::strict_encode64(ds_msg)
138

  
139
            result, info = do_action(id, nil, ds_mad, :export,
140
                "#{ds_msg64} #{id}", false)
141

  
142
            if ( result == RESULT[:failure] )
143
                failure(:import, id, "Error exporting image to file: #{info}")
178 144
                return
145
            end
146

  
147
            info_doc = OpenNebula::XMLElement.new
148
            info_doc.initialize_xml(info, 'IMPORT_INFO')
149
        #-----------------------------------------------------------------------
150
        # Only IMAGE type is supported
151
        #-----------------------------------------------------------------------
152
        else
153
            failure(:import, id, "Type #{apptype} not supported")
154
            return
179 155
        end
180 156

  
157
        # --------------- Import image app into the marketplace ----------------
181 158
        xml.add_element('/MARKET_DRIVER_ACTION_DATA',
182
            'IMPORT_SOURCE' => "#{source}")
159
                        'IMPORT_SOURCE' => "#{info_doc['IMPORT_SOURCE']}",
160
                        'CHECKSUM'      => "#{info_doc['CHECKSUM']}",
161
                        'SIZE'          => "#{info_doc['SIZE']}",
162
                        'FORMAT'        => "#{info_doc['FORMAT']}",
163
                        'DISPOSE'       => "#{info_doc['DISPOSE']}")
164

  
183 165
        mp_msg64 = Base64::strict_encode64(xml.to_xml)
184 166

  
185 167
        result, info = do_action(id, mp_mad, nil, :import, "#{mp_msg64} #{id}",
......
236 218

  
237 219
        result, info = get_info_from_execution(rc)
238 220

  
239
        info = Base64::strict_encode64(info) if encode
221
        info = Base64::strict_encode64(info) if encode && result != RESULT[:failure]
240 222

  
241 223
        return result, info
242 224
    end
src/market_mad/remotes/http/import
35 35
DRIVER_PATH=$(dirname $0)
36 36
source ${DRIVER_PATH}/../../datastore/libfs.sh
37 37

  
38
XPATH_PATH="${DRIVER_PATH}/../../datastore"
38
UTILS_PATH="${DRIVER_PATH}/../../datastore"
39 39

  
40 40
# -------- Get arguments from OpenNebula core ------------
41 41

  
42 42
DRV_ACTION=$1
43 43
ID=$2
44 44

  
45
XPATH="$XPATH_PATH/xpath.rb -b $DRV_ACTION"
45
XPATH="$UTILS_PATH/xpath.rb -b $DRV_ACTION"
46 46

  
47 47
unset i XPATH_ELEMENTS
48 48

  
49 49
while IFS= read -r -d '' element; do
50 50
    XPATH_ELEMENTS[i++]="$element"
51 51
done < <($XPATH     /MARKET_DRIVER_ACTION_DATA/IMPORT_SOURCE \
52
                    /MARKET_DRIVER_ACTION_DATA/FORMAT \
53
                    /MARKET_DRIVER_ACTION_DATA/DISPOSE \
54
                    /MARKET_DRIVER_ACTION_DATA/SIZE \
55
                    /MARKET_DRIVER_ACTION_DATA/CHECKSUM \
52 56
                    /MARKET_DRIVER_ACTION_DATA/MARKETPLACE/TEMPLATE/BASE_URL \
57
                    /MARKET_DRIVER_ACTION_DATA/MARKETPLACE/TEMPLATE/BRIDGE_LIST \
53 58
                    /MARKET_DRIVER_ACTION_DATA/MARKETPLACE/TEMPLATE/PUBLIC_DIR)
54 59
unset i
55 60

  
56 61
IMPORT_SOURCE="${XPATH_ELEMENTS[i++]}"
62
FORMAT="${XPATH_ELEMENTS[i++]}"
63
DISPOSE="${XPATH_ELEMENTS[i++]}"
64
SIZE="${XPATH_ELEMENTS[i++]}"
65
CHECKSUM="${XPATH_ELEMENTS[i++]}"
57 66
BASE_URL="${XPATH_ELEMENTS[i++]}"
67
BRIDGE_LIST="${XPATH_ELEMENTS[i++]}"
58 68
PUBLIC_DIR="${XPATH_ELEMENTS[i++]}"
59 69

  
60 70
# -------- Copy source to public folder an generarte App data ------------
......
63 73
DST_PATH="${PUBLIC_DIR}/${APPNAME}"
64 74
SOURCE="${BASE_URL}/${APPNAME}"
65 75

  
66
if [ ! -f ${IMPORT_SOURCE} ]; then
67
    log_error "File ${IMPORT_SOURCE} does not exits"
68
    exit -1
69
fi
76
if [ -n "$BRIDGE_LIST" ]; then
77
    DST_HOST=`get_destination_host $ID`
78
    CP_CMD="$UTILS_PATH/downloader.sh ${IMPORT_SOURCE} -"
70 79

  
71
CHECKSUM=$(${MD5SUM} ${IMPORT_SOURCE} | cut -f1 -d' ')
80
    exec_and_log "eval $CP_CMD | $SSH ${DST_HOST} $DD of=${DST_PATH} bs=64k" \
81
                 "Error dumping ${IMPORT_SOURCE} to ${DST_HOST}:${DST_PATH}"
72 82

  
73
SIZE=$(fs_size ${IMPORT_SOURCE})
83
    #if [ "${DISPOSE}" = "YES" ]; then
84
    #TODO This should call a ds operation
85
    #fi
86
else
87
    CP_CMD="$UTILS_PATH/downloader.sh ${IMPORT_SOURCE} ${DST_PATH}"
88
    exec_and_log "$CP_CMD" "Error copying ${IMPORT_SOURCE} to ${DST_PATH}"
74 89

  
75
if [ "$SIZE" = "0" ]; then
76
    log_error "Cannot determine size for ${IMPORT_SOURCE}"
77
    exit -1
90
    if [ "${DISPOSE}" = "YES" ]; then
91
    #TODO This should call a ds operation
92
        exec_and_log "$RM ${IMPORT_SOURCE}" "Error removing ${IMPORT_SOURCE}"
93
    fi
78 94
fi
79 95

  
80
exec_and_log "$CP ${IMPORT_SOURCE} ${DST_PATH}" \
81
    "Error copying ${IMPORT_SOURCE} to ${DST_PATH}. ${IMPORT_SOURCE} not removed"
82

  
83
exec_and_log "$RM ${IMPORT_SOURCE}" "Error removing ${IMPORT_SOURCE}"
84

  
85 96
RESPONSE=$(cat << EOF
86 97
SOURCE="$SOURCE"
87 98
CHECKSUM="$CHECKSUM"
88 99
SIZE="$SIZE"
100
FORMAT="$FORMAT"
89 101
EOF
90 102
)
91 103

  
src/nebula/Nebula.cc
936 936

  
937 937
    lcm->init_managers();
938 938

  
939
    marketm->init_managers();
940

  
939 941
    // ---- Start the Request Manager ----
940 942

  
941 943
    rc = rm->start();

Also available in: Unified diff