1
/*******************************************************************************
2
*Copyright (c) 2009 Eucalyptus Systems, Inc.
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation, only version 3 of the License.
9
* This file is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* You should have received a copy of the GNU General Public License along
15
* with this program. If not, see <http://www.gnu.org/licenses/>.
17
* Please contact Eucalyptus Systems, Inc., 130 Castilian
18
* Dr., Goleta, CA 93101 USA or visit <http://www.eucalyptus.com/licenses/>
19
* if you need additional information or have any questions.
21
* This file may incorporate work covered under the following copyright and
24
* Software License Agreement (BSD License)
26
* Copyright (c) 2008, Regents of the University of California
27
* All rights reserved.
29
* Redistribution and use of this software in source and binary forms, with
30
* or without modification, are permitted provided that the following
33
* Redistributions of source code must retain the above copyright notice,
34
* this list of conditions and the following disclaimer.
36
* Redistributions in binary form must reproduce the above copyright
37
* notice, this list of conditions and the following disclaimer in the
38
* documentation and/or other materials provided with the distribution.
40
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
41
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
42
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
43
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
44
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
46
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
47
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
48
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
49
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
50
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. USERS OF
51
* THIS SOFTWARE ACKNOWLEDGE THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE
52
* LICENSED MATERIAL, COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS
53
* SOFTWARE, AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
54
* IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, SANTA
55
* BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, WHICH IN
56
* THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION, REPLACEMENT
57
* OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO IDENTIFIED, OR
58
* WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT NEEDED TO COMPLY WITH
59
* ANY SUCH LICENSES OR RIGHTS.
60
*******************************************************************************/
63
* Author: Neil Soman neil@eucalyptus.com
66
package edu.ucsb.eucalyptus.storage.fs;
69
import java.util.NavigableSet;
71
import org.apache.log4j.Logger;
73
import com.eucalyptus.component.Component;
74
import com.eucalyptus.component.Components;
75
import com.eucalyptus.component.Service;
76
import com.eucalyptus.component.ServiceConfiguration;
77
import com.eucalyptus.component.id.Walrus;
78
import com.eucalyptus.util.EucalyptusCloudException;
79
import java.util.concurrent.ExecutionException;
80
import com.eucalyptus.util.WalrusProperties;
82
import edu.ucsb.eucalyptus.cloud.entities.DRBDInfo;
83
import edu.ucsb.eucalyptus.cloud.entities.WalrusInfo;
84
import edu.ucsb.eucalyptus.storage.fs.FileSystemStorageManager;
85
import edu.ucsb.eucalyptus.util.SystemUtil;
87
public class DRBDStorageManager extends FileSystemStorageManager {
89
private static Logger LOG = Logger.getLogger( DRBDStorageManager.class );
90
private static final String PRIMARY_ROLE = "Primary";
91
private static final String SECONDARY_ROLE = "Secondary";
92
private static final String DSTATE_UPTODATE = "UpToDate";
93
private static final String DSTATE_UNKNOWN = "Unknown";
94
private static final String CSTATE_WFCONNECTION = "WFConnection";
95
private static final String CSTATE_CONNECTED = "Connected";
97
public DRBDStorageManager() {
100
private String getConnectionStatus() throws ExecutionException, EucalyptusCloudException {
101
String returnValue = SystemUtil.run(new String[]{WalrusProperties.eucaHome + WalrusProperties.EUCA_ROOT_WRAPPER, "drbdadm", "cstate", DRBDInfo.getDRBDInfo().getResource()});
102
if(returnValue.length() == 0) {
103
throw new EucalyptusCloudException("Unable to get connection status for resource: " + DRBDInfo.getDRBDInfo().getResource());
108
private String getDataStatus() throws ExecutionException, EucalyptusCloudException {
109
String returnValue = SystemUtil.run(new String[]{WalrusProperties.eucaHome + WalrusProperties.EUCA_ROOT_WRAPPER, "drbdadm", "dstate", DRBDInfo.getDRBDInfo().getResource()});
110
if(returnValue.length() == 0) {
111
throw new EucalyptusCloudException("Unable to get data status for resource: " + DRBDInfo.getDRBDInfo().getResource());
118
* @throws ExecutionException
119
* @throws EucalyptusCloudException
121
private String getRole() throws ExecutionException, EucalyptusCloudException {
122
String returnValue = SystemUtil.run(new String[]{WalrusProperties.eucaHome + WalrusProperties.EUCA_ROOT_WRAPPER, "drbdadm", "role", DRBDInfo.getDRBDInfo().getResource()});
123
if(returnValue.length() == 0) {
124
throw new EucalyptusCloudException("Unable to get role for resource: " + DRBDInfo.getDRBDInfo().getResource());
129
/*This method does not check if resource is already primary*/
131
* @throws ExecutionException
132
* @throws EucalyptusCloudException
134
private void makePrimary() throws ExecutionException, EucalyptusCloudException {
135
if(SystemUtil.runAndGetCode(new String[]{WalrusProperties.eucaHome + WalrusProperties.EUCA_ROOT_WRAPPER, "drbdadm", "primary", DRBDInfo.getDRBDInfo().getResource()}) != 0) {
136
throw new EucalyptusCloudException("Unable to make resource " + DRBDInfo.getDRBDInfo().getResource() + " primary");
140
/*This method does not check if resource is already secondary*/
142
* @throws ExecutionException
143
* @throws EucalyptusCloudException
145
private void makeSecondary() throws ExecutionException, EucalyptusCloudException {
146
if(SystemUtil.runAndGetCode(new String[]{WalrusProperties.eucaHome + WalrusProperties.EUCA_ROOT_WRAPPER, "drbdadm", "secondary", DRBDInfo.getDRBDInfo().getResource()}) != 0) {
147
throw new EucalyptusCloudException("Unable to make resource " + DRBDInfo.getDRBDInfo().getResource() + " secondary");
151
private void connectResource() throws ExecutionException, EucalyptusCloudException {
152
if(SystemUtil.runAndGetCode(new String[]{WalrusProperties.eucaHome + WalrusProperties.EUCA_ROOT_WRAPPER, "drbdadm", "connect", DRBDInfo.getDRBDInfo().getResource()}) != 0) {
153
throw new EucalyptusCloudException("Unable to connect resource: " + DRBDInfo.getDRBDInfo().getResource());
157
private void disconnectResource() throws ExecutionException, EucalyptusCloudException {
158
if(SystemUtil.runAndGetCode(new String[]{WalrusProperties.eucaHome + WalrusProperties.EUCA_ROOT_WRAPPER, "drbdadm", "disconnect", DRBDInfo.getDRBDInfo().getResource()}) != 0) {
159
throw new EucalyptusCloudException("Unable to disconnect resource: " + DRBDInfo.getDRBDInfo().getResource());
164
private void generateConfig() {
168
private void mountPrimary() throws ExecutionException, EucalyptusCloudException {
169
if(SystemUtil.runAndGetCode(new String[]{WalrusProperties.eucaHome + WalrusProperties.EUCA_MOUNT_WRAPPER, "mount", DRBDInfo.getDRBDInfo().getBlockDevice(), WalrusInfo.getWalrusInfo().getStorageDir(), WalrusProperties.EUCA_USER}) != 0) {
170
throw new EucalyptusCloudException("Unable to mount " + DRBDInfo.getDRBDInfo().getBlockDevice() + " as " + WalrusInfo.getWalrusInfo().getStorageDir());
172
SystemUtil.setEucaReadWriteOnly(WalrusInfo.getWalrusInfo().getStorageDir());
175
private void unmountPrimary() throws ExecutionException, EucalyptusCloudException {
176
if(SystemUtil.runAndGetCode(new String[]{WalrusProperties.eucaHome + WalrusProperties.EUCA_MOUNT_WRAPPER, "umount", WalrusInfo.getWalrusInfo().getStorageDir()}) != 0) {
177
throw new EucalyptusCloudException("Unable to unmount " + DRBDInfo.getDRBDInfo().getBlockDevice());
182
* We use /proc/mounts because EUCA_ROOT_WRAPPER uses a syscall and does not update /etc/mtab
184
* @throws ExecutionException
186
private boolean isMounted() throws EucalyptusCloudException {
187
String returnValue = SystemUtil.run(new String[]{WalrusProperties.eucaHome + WalrusProperties.EUCA_ROOT_WRAPPER, "cat", "/proc/mounts"});
188
if(returnValue.length() > 0) {
189
if(returnValue.contains(DRBDInfo.getDRBDInfo().getBlockDevice())) {
196
private boolean isPrimary() throws EucalyptusCloudException, ExecutionException {
197
String roleString = getRole();
198
String[] roleParts = roleString.split("/");
199
if(roleParts.length > 1) {
200
if(roleParts[0].startsWith(PRIMARY_ROLE)) {
206
throw new EucalyptusCloudException("Unable to parse role.");
210
private boolean isSecondary() throws EucalyptusCloudException, ExecutionException {
211
String roleString = getRole();
212
String[] roleParts = roleString.split("/");
213
if(roleParts.length > 1) {
214
if(roleParts[0].startsWith(SECONDARY_ROLE)) {
220
throw new EucalyptusCloudException("Unable to parse role.");
224
private boolean isPeerPrimary() throws EucalyptusCloudException, ExecutionException {
225
String roleString = getRole();
226
String[] roleParts = roleString.split("/");
227
if(roleParts.length > 1) {
228
if(roleParts[1].startsWith(PRIMARY_ROLE)) {
234
throw new EucalyptusCloudException("Unable to parse role.");
238
private boolean isPeerSecondary() throws EucalyptusCloudException, ExecutionException {
239
String roleString = getRole();
240
String[] roleParts = roleString.split("/");
241
if(roleParts.length > 1) {
242
if(roleParts[1].startsWith(SECONDARY_ROLE)) {
248
throw new EucalyptusCloudException("Unable to parse role.");
252
private boolean isConnected() throws ExecutionException, EucalyptusCloudException {
253
String cstateString = getConnectionStatus();
254
if((cstateString != null) && cstateString.startsWith(CSTATE_CONNECTED)) {
261
private boolean isUpToDate() throws EucalyptusCloudException, ExecutionException {
262
String dstateString = getDataStatus();
263
String[] dstateParts = dstateString.split("/");
264
if(dstateParts.length > 1) {
265
if(dstateParts[0].startsWith(DSTATE_UPTODATE) && (dstateParts[1].startsWith(DSTATE_UPTODATE))) {
271
throw new EucalyptusCloudException("Unable to get resource dstate.");
275
private void checkLocalDisk() throws EucalyptusCloudException {
276
String blockDevice = DRBDInfo.getDRBDInfo().getBlockDevice();
277
File mount = new File(blockDevice);
278
if(!mount.exists()) {
279
throw new EucalyptusCloudException("Block device " + blockDevice + " not found.");
281
String storageDir = WalrusInfo.getWalrusInfo().getStorageDir();
282
File root = new File(storageDir);
284
throw new EucalyptusCloudException("Storage directory " + storageDir + " not found.");
288
public void becomeMaster() throws EucalyptusCloudException, ExecutionException {
290
//role, cstate, dstate
293
if(isPeerPrimary()) {
294
throw new EucalyptusCloudException("Peer is primary and I am supposed to be master! Unable to proceed!");
301
} catch (Exception e) {
309
} catch(Exception e) {
318
throw new EucalyptusCloudException("Unable to make resource primary.");
322
public void becomeSlave() throws EucalyptusCloudException, ExecutionException {
324
//check mount point, block device, role, cstate, dstate
335
} catch(Exception e) {
341
throw new EucalyptusCloudException("Unable to make resource secondary.");
343
if(!isPeerPrimary()) {
344
LOG.warn("Warning! Peer is not primary. No usable component?");
349
public void secondaryDrasticRecovery() throws ExecutionException, EucalyptusCloudException {
350
if(SystemUtil.runAndGetCode(new String[]{WalrusProperties.eucaHome + WalrusProperties.EUCA_ROOT_WRAPPER, "drbdadm", "--", "--discard-my-data", "connect", DRBDInfo.getDRBDInfo().getResource()}) != 0) {
351
throw new EucalyptusCloudException("Unable to recover from split brain for resource: " + DRBDInfo.getDRBDInfo().getResource());
356
public void enable() throws EucalyptusCloudException {
359
} catch (ExecutionException e) {
360
throw new EucalyptusCloudException(e);
365
public void disable() throws EucalyptusCloudException {
368
} catch (ExecutionException e) {
369
throw new EucalyptusCloudException(e);
375
public void check() throws EucalyptusCloudException {
377
boolean notConnected = false;
381
} catch(Exception e) {
385
if (Component.State.ENABLED.equals(Components.lookup(Walrus.class).getState())) {
387
throw new EucalyptusCloudException("I am the master, but not DRBD primary. Please make me primary. Aborting!");
390
if((isConnected()) && (!isUpToDate())) {
391
throw new EucalyptusCloudException("Resource connected but not up to date!");
393
if (Component.State.DISABLED.equals(Components.lookup(Walrus.class).getState())) {
395
LOG.warn("I am the slave, but not DRBD secondary. Trying to become secondary...");
402
throw new EucalyptusCloudException("Attempt to set secondary failed. Unable to proceed!");
407
} catch(ExecutionException ex) {
408
throw new EucalyptusCloudException(ex);
413
public void start() throws EucalyptusCloudException {
416
} catch (ExecutionException e) {
417
throw new EucalyptusCloudException(e);
422
public void stop() throws EucalyptusCloudException {
431
} catch(ExecutionException ex) {
432
throw new EucalyptusCloudException(ex);