Login
[x]
Log in using an account from:
Fedora Account System
Red Hat Associate
Red Hat Customer
Or login using a Red Hat Bugzilla account
Forgot Password
Login:
Hide Forgot
Create an Account
Red Hat Bugzilla – Attachment 149757 Details for
Bug 231692
rgmanager live migration of vms doesn't work
[?]
New
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
|
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh83 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
This site requires JavaScript to be enabled to function correctly, please enable it.
[patch]
Enables vm live migration.
rgmanager-rhel5-vm-migrate-merge.patch (text/plain), 17.72 KB, created by
Lon Hohberger
on 2007-03-10 00:33:12 UTC
(
hide
)
Description:
Enables vm live migration.
Filename:
MIME Type:
Creator:
Lon Hohberger
Created:
2007-03-10 00:33:12 UTC
Size:
17.72 KB
patch
obsolete
>Index: include/res-ocf.h >=================================================================== >RCS file: /cvs/cluster/cluster/rgmanager/include/res-ocf.h,v >retrieving revision 1.1 >diff -u -r1.1 res-ocf.h >--- include/res-ocf.h 13 Aug 2004 15:36:50 -0000 1.1 >+++ include/res-ocf.h 10 Mar 2007 00:26:16 -0000 >@@ -45,4 +45,22 @@ > #define OCF_RA_NOT_RUNNING 7 > #define OCF_RA_MAX 7 > >+/* >+ Resource operations - not ocf-specified >+ */ >+#define RS_START (0) >+#define RS_STOP (1) >+#define RS_STATUS (2) >+#define RS_RESINFO (3) >+#define RS_RESTART (4) >+#define RS_RELOAD (5) >+#define RS_CONDRESTART (6) >+#define RS_RECOVER (7) >+#define RS_CONDSTART (8) /** Start if flagged with RF_NEEDSTART */ >+#define RS_CONDSTOP (9) /** STOP if flagged with RF_NEEDSTOP */ >+#define RS_MONITOR (10) >+#define RS_META_DATA (11) >+#define RS_VALIDATE (12) >+#define RS_MIGRATE (13) >+ > #endif >Index: include/resgroup.h >=================================================================== >RCS file: /cvs/cluster/cluster/rgmanager/include/resgroup.h,v >retrieving revision 1.15.2.2 >diff -u -r1.15.2.2 resgroup.h >--- include/resgroup.h 14 Dec 2006 22:17:43 -0000 1.15.2.2 >+++ include/resgroup.h 10 Mar 2007 00:26:16 -0000 >@@ -81,9 +81,7 @@ > #define RG_MIGRATE 22 > #define RG_NONE 999 > >-extern const char *rg_req_strings[]; >- >-#define rg_req_str(req) (rg_req_strings[req]) >+const char *rg_req_str(int req); > > int handle_relocate_req(char *svcName, int request, int preferred_target, > int *new_owner); >@@ -107,9 +105,8 @@ > > #define DEFAULT_CHECK_INTERVAL 10 > >-extern const char *rg_state_strings[]; >- >-#define rg_state_str(state) (rg_state_strings[state - RG_STATE_BASE]) >+const char *rg_state_str(int val); >+const char *agent_op_str(int val); > > int rg_status(const char *resgroupname); > int group_op(char *rgname, int op); >@@ -175,7 +172,7 @@ > #define RG_YES 1 > #define RG_NO 2 > >-char *rg_strerror(int val); >+const char *rg_strerror(int val); > > > /* >Index: include/reslist.h >=================================================================== >RCS file: /cvs/cluster/cluster/rgmanager/include/reslist.h,v >retrieving revision 1.15 >diff -u -r1.15 reslist.h >--- include/reslist.h 27 Sep 2006 16:28:41 -0000 1.15 >+++ include/reslist.h 10 Mar 2007 00:26:16 -0000 >@@ -40,25 +40,6 @@ > #define RES_STARTED (1) > #define RES_FAILED (2) > >-/* >- Resource operations >- */ >-#define RS_START (0) >-#define RS_STOP (1) >-#define RS_STATUS (2) >-#define RS_RESINFO (3) >-#define RS_RESTART (4) >-#define RS_RELOAD (5) >-#define RS_CONDRESTART (6) >-#define RS_RECOVER (7) >-#define RS_CONDSTART (8) /** Start if flagged with RF_NEEDSTART */ >-#define RS_CONDSTOP (9) /** STOP if flagged with RF_NEEDSTOP */ >-#define RS_MONITOR (10) >-#define RS_META_DATA (11) >-#define RS_VALIDATE (12) >-#define RS_MIGRATE (13) >- >- > #ifndef SHAREDIR > #define SHAREDIR "/usr/share/rgmanager" > #endif >Index: src/clulib/rg_strings.c >=================================================================== >RCS file: /cvs/cluster/cluster/rgmanager/src/clulib/rg_strings.c,v >retrieving revision 1.5.2.1 >diff -u -r1.5.2.1 rg_strings.c >--- src/clulib/rg_strings.c 14 Dec 2006 22:17:43 -0000 1.5.2.1 >+++ src/clulib/rg_strings.c 10 Mar 2007 00:26:17 -0000 >@@ -16,9 +16,16 @@ > Free Software Foundation, Inc., 675 Mass Ave, Cambridge, > MA 02139, USA. > */ >+#include <res-ocf.h> > #include <resgroup.h> > >-struct { int val; char *str; } rg_error_strings[] = { >+struct string_val { >+ int val; >+ char *str; >+}; >+ >+ >+const struct string_val rg_error_strings[] = { > { RG_ERUN, "Service is already running" }, > { RG_EQUORUM, "Operation requires quorum" }, > { RG_EINVAL, "Invalid operation for resource" }, >@@ -36,53 +43,107 @@ > }; > > >-char *rg_strerror(int err) >+const struct string_val rg_req_strings[] = { >+ {RG_SUCCESS, "success" }, >+ {RG_FAIL, "fail"}, >+ {RG_START, "start"}, >+ {RG_STOP, "stop"}, >+ {RG_STATUS, "status"}, >+ {RG_DISABLE, "disable"}, >+ {RG_STOP_RECOVER, "stop (recovery)"}, >+ {RG_START_RECOVER, "start (recovery)"}, >+ {RG_RESTART, "restart"}, >+ {RG_EXITING, "exiting"}, >+ {RG_INIT, "initialize"}, >+ {RG_ENABLE, "enable"}, >+ {RG_STATUS_NODE, "status inquiry"}, >+ {RG_RELOCATE, "relocate"}, >+ {RG_CONDSTOP, "conditional stop"}, >+ {RG_CONDSTART, "conditional start"}, >+ {RG_START_REMOTE,"remote start"}, >+ {RG_STOP_USER, "user stop"}, >+ {RG_STOP_EXITING, "stop (shutdown)"}, >+ {RG_LOCK, "locking"}, >+ {RG_UNLOCK, "unlocking"}, >+ {RG_QUERY_LOCK, "lock status inquiry"}, >+ {RG_MIGRATE, "migrate"}, >+ {RG_NONE, "none"}, >+ {0, NULL} >+}; >+ >+ >+const struct string_val rg_state_strings[] = { >+ {RG_STATE_STOPPED, "stopped"}, >+ {RG_STATE_STARTING, "starting"}, >+ {RG_STATE_STARTED, "started"}, >+ {RG_STATE_STOPPING, "stopping"}, >+ {RG_STATE_FAILED, "failed"}, >+ {RG_STATE_UNINITIALIZED, "uninitialized"}, >+ {RG_STATE_CHECK, "checking"}, >+ {RG_STATE_ERROR, "recoverable"}, >+ {RG_STATE_RECOVER, "recovering"}, >+ {RG_STATE_DISABLED, "disabled"}, >+ {RG_STATE_MIGRATE, "migrating"}, >+ {0, NULL} >+}; >+ >+ >+const struct string_val agent_ops[] = { >+ {RS_START, "start"}, >+ {RS_STOP, "stop"}, >+ {RS_STATUS, "status"}, >+ {RS_RESINFO, "resinfo"}, >+ {RS_RESTART, "restart"}, >+ {RS_RELOAD, "reload"}, >+ {RS_CONDRESTART, "condrestart"}, /* Unused */ >+ {RS_RECOVER, "recover"}, >+ {RS_CONDSTART, "condstart"}, >+ {RS_CONDSTOP, "condstop"}, >+ {RS_MONITOR, "monitor"}, >+ {RS_META_DATA, "meta-data"}, /* printenv */ >+ {RS_VALIDATE, "validate-all"}, >+ {RS_MIGRATE, "migrate"}, >+ {0 , NULL} >+}; >+ >+ >+static inline const char * >+rg_search_table(const struct string_val *table, int val) > { > int x; > >- for (x = 0; rg_error_strings[x].str != NULL; x++) { >- if (rg_error_strings[x].val == err) { >- return rg_error_strings[x].str; >+ for (x = 0; table[x].str != NULL; x++) { >+ if (table[x].val == val) { >+ return table[x].str; > } > } > > return "Unknown"; > } >+ >+ >+const char * >+rg_strerror(int val) >+{ >+ return rg_search_table(rg_error_strings, val); >+} > >+const char * >+rg_state_str(int val) >+{ >+ return rg_search_table(rg_state_strings, val); >+} > >-const char *rg_state_strings[] = { >- "stopped", >- "starting", >- "started", >- "stopping", >- "failed", >- "uninitialized", >- "checking", >- "recoverable", >- "recovering", >- "disabled", >- "" >-}; > >-const char *rg_req_strings[] = { >- "success", >- "fail", >- "start", >- "stop", >- "status", >- "disable", >- "stop (recovery)", >- "start (recovery)", >- "restart", >- "exiting", >- "initialize", >- "enable", >- "status inquiry", >- "relocate", >- "conditional stop", >- "conditional start", >- "remote start", >- "user stop", >- "" >-}; >+const char * >+rg_req_str(int val) >+{ >+ return rg_search_table(rg_req_strings, val); >+} > >+ >+const char * >+agent_op_str(int val) >+{ >+ return rg_search_table(agent_ops, val); >+} >Index: src/daemons/groups.c >=================================================================== >RCS file: /cvs/cluster/cluster/rgmanager/src/daemons/groups.c,v >retrieving revision 1.25.2.2 >diff -u -r1.25.2.2 groups.c >--- src/daemons/groups.c 21 Feb 2007 20:46:49 -0000 1.25.2.2 >+++ src/daemons/groups.c 10 Mar 2007 00:26:17 -0000 >@@ -36,7 +36,6 @@ > #define cn_svccount cn_address.cna_address[0] /* Theses are uint8_t size */ > #define cn_svcexcl cn_address.cna_address[1] > >-extern char *res_ops[]; > static int config_version = 0; > static resource_t *_resources = NULL; > static resource_rule_t *_rules = NULL; >@@ -734,9 +733,10 @@ > int > group_migrate(char *groupname, int target) > { >+ resource_node_t *rn = NULL, *tmp; > resource_t *res; > char *tgt_name; >- int ret = RG_ENOSERVICE; >+ int ret = RG_ENOSERVICE, x = 0; > cluster_member_list_t *membership; > > membership = member_list(); >@@ -746,15 +746,26 @@ > pthread_rwlock_rdlock(&resource_lock); > > tgt_name = memb_id_to_name(membership, target); >+ if (!tgt_name) { >+ ret = RG_EINVAL; >+ goto out; >+ } >+ > res = find_root_by_ref(&_resources, groupname); > if (!res) > goto out; > >- if (!tgt_name) { >- ret = RG_EINVAL; >+ list_do(&_tree, tmp) { >+ if (tmp->rn_resource == res) { >+ rn = tmp; >+ break; >+ } >+ } while (!list_done(&_tree, tmp)); >+ >+ if (!rn) > goto out; >- } >- ret = res_exec(res, res_ops[RG_MIGRATE], tgt_name); >+ >+ ret = res_exec(rn, agent_op_str(RS_MIGRATE), tgt_name); > > out: > pthread_rwlock_unlock(&resource_lock); >@@ -1009,7 +1020,8 @@ > } > > if (svcblk.rs_owner != my_id() || >- svcblk.rs_state != RG_STATE_STARTED) >+ (svcblk.rs_state != RG_STATE_STARTED && >+ svcblk.rs_state != RG_STATE_MIGRATE)) > continue; > > rt_enqueue_request(rg, RG_STATUS, >Index: src/daemons/restree.c >=================================================================== >RCS file: /cvs/cluster/cluster/rgmanager/src/daemons/restree.c,v >retrieving revision 1.23 >diff -u -r1.23 restree.c >--- src/daemons/restree.c 21 Sep 2006 18:04:04 -0000 1.23 >+++ src/daemons/restree.c 10 Mar 2007 00:26:17 -0000 >@@ -60,23 +60,6 @@ > time_t get_time(char *action, int depth, resource_node_t *node); > > >-const char *res_ops[] = { >- "start", >- "stop", >- "status", >- "resinfo", >- "restart", >- "reload", >- "condrestart", /* Unused */ >- "recover", >- "condstart", >- "condstop", >- "monitor", >- "meta-data", /* printenv */ >- "validate-all", >- "migrate" >-}; >- > > const char *ocf_errors[] = { > "success", // 0 >@@ -97,14 +80,13 @@ > const char * > ocf_strerror(int ret) > { >- if (ret < OCF_RA_MAX) >+ if (ret >= 0 && ret < OCF_RA_MAX) > return ocf_errors[ret]; > > return "unspecified"; > } > > >- > /** > Destroys an environment variable array. > >@@ -360,7 +342,7 @@ > #endif > #if 0 > printf("Exec of script %s, action %s type %s\n", >- res->r_rule->rr_agent, res_ops[op], >+ res->r_rule->rr_agent, agent_op_str(op), > res->r_rule->rr_type); > #endif > >@@ -701,7 +683,7 @@ > > #if 0 > printf("%s children of %s type %s (level %d)\n", >- res_ops[op], >+ agent_op_str(op), > node->rn_resource->r_rule->rr_type, > rule->rr_childtypes[x].rc_name, l); > #endif >@@ -744,7 +726,7 @@ > > /* > printf("%s children of %s type %s (default level)\n", >- res_ops[op], >+ agent_op_str(op), > node->rn_resource->r_rule->rr_type, > rule->rr_childtypes[x].rc_name); > */ >@@ -852,7 +834,7 @@ > return 0; > > node->rn_actions[idx].ra_last = now; >- if ((x = res_exec(node, res_ops[RS_STATUS], NULL, >+ if ((x = res_exec(node, agent_op_str(RS_STATUS), NULL, > node->rn_actions[idx].ra_depth)) == 0) > return 0; > >@@ -860,7 +842,7 @@ > return x; > > /* Strange/failed status. Try to recover inline. */ >- if ((x = res_exec(node, res_ops[RS_RECOVER], NULL, 0)) == 0) >+ if ((x = res_exec(node, agent_op_str(RS_RECOVER), NULL, 0)) == 0) > return 0; > > return x; >@@ -965,7 +947,7 @@ > me = !first || (node->rn_resource == first); > > /* >- printf("begin %s: %s %s [0x%x]\n", res_ops[op], >+ printf("begin %s: %s %s [0x%x]\n", agent_op_str(op), > node->rn_resource->r_rule->rr_type, > primary_attr_value(node->rn_resource), > node->rn_flags); >@@ -1010,7 +992,7 @@ > if (me && (op == RS_START)) { > node->rn_flags &= ~RF_NEEDSTART; > >- rv = res_exec(node, res_ops[op], NULL, 0); >+ rv = res_exec(node, agent_op_str(op), NULL, 0); > if (rv != 0) { > node->rn_state = RES_FAILED; > return rv; >@@ -1034,7 +1016,7 @@ > /* Stop/status/etc stops after children have stopped */ > if (me && (op == RS_STOP)) { > node->rn_flags &= ~RF_NEEDSTOP; >- rv = res_exec(node, res_ops[op], NULL, 0); >+ rv = res_exec(node, agent_op_str(op), NULL, 0); > > if (rv != 0) { > node->rn_state = RES_FAILED; >@@ -1054,7 +1036,7 @@ > } > > /* >- printf("end %s: %s %s\n", res_ops[op], >+ printf("end %s: %s %s\n", agent_op_str(op), > node->rn_resource->r_rule->rr_type, > primary_attr_value(node->rn_resource)); > */ >Index: src/daemons/rg_state.c >=================================================================== >RCS file: /cvs/cluster/cluster/rgmanager/src/daemons/rg_state.c,v >retrieving revision 1.24.2.4 >diff -u -r1.24.2.4 rg_state.c >--- src/daemons/rg_state.c 20 Feb 2007 19:55:12 -0000 1.24.2.4 >+++ src/daemons/rg_state.c 10 Mar 2007 00:26:18 -0000 >@@ -866,6 +866,10 @@ > rg_unlock(&lockp); > > ret = group_migrate(svcName, target); >+ >+ if (ret) >+ svc_fail(svcName); >+ > return ret; > } > >Index: src/daemons/rg_thread.c >=================================================================== >RCS file: /cvs/cluster/cluster/rgmanager/src/daemons/rg_thread.c,v >retrieving revision 1.15.2.2 >diff -u -r1.15.2.2 rg_thread.c >--- src/daemons/rg_thread.c 14 Dec 2006 20:12:25 -0000 1.15.2.2 >+++ src/daemons/rg_thread.c 10 Mar 2007 00:26:18 -0000 >@@ -163,7 +163,7 @@ > char myname[256]; > resthread_t *myself; > request_t *req; >- uint32_t ret = RG_FAIL, error = 0; >+ int ret = RG_EFAIL, error = 0; > > rg_inc_threads(); > >@@ -274,7 +274,7 @@ > /* > * Bad news. > */ >- ret = RG_FAIL; >+ ret = RG_EFAIL; > } > break; > >@@ -289,7 +289,7 @@ > if (error == 0) > ret = RG_SUCCESS; > else >- ret = RG_FAIL; >+ ret = RG_EFAIL; > break; > > case RG_CONDSTOP: >@@ -321,7 +321,7 @@ > /* > * Bad news. > */ >- ret = RG_FAIL; >+ ret = RG_EFAIL; > } > > break; >@@ -340,7 +340,7 @@ > /* > * Bad news. > */ >- ret = RG_FAIL; >+ ret = RG_EFAIL; > } > > pthread_mutex_lock(&my_queue_mutex); >@@ -368,7 +368,7 @@ > /* > * Bad news. > */ >- ret = RG_FAIL; >+ ret = RG_EFAIL; > } > > break; >@@ -393,7 +393,7 @@ > /* > * Bad news. > */ >- ret = RG_FAIL; >+ ret = RG_EFAIL; > } > > break; >Index: src/daemons/test.c >=================================================================== >RCS file: /cvs/cluster/cluster/rgmanager/src/daemons/test.c,v >retrieving revision 1.6 >diff -u -r1.6 test.c >--- src/daemons/test.c 19 Jul 2006 18:43:32 -0000 1.6 >+++ src/daemons/test.c 10 Mar 2007 00:26:18 -0000 >@@ -83,7 +83,7 @@ > fod_t *domains = NULL; > resource_rule_t *rulelist = NULL, *currule; > resource_t *reslist = NULL, *curres; >- resource_node_t *tree = NULL; >+ resource_node_t *tree = NULL, *tmp, *rn = NULL; > int ccsfd, ret = 0, rules = 0; > > fprintf(stderr,"Running in test mode.\n"); >@@ -145,6 +145,13 @@ > goto out; > } > >+ list_do(&tree, tmp) { >+ if (tmp->rn_resource == curres) { >+ rn = tmp; >+ break; >+ } >+ } while (!list_done(&tree, tmp)); >+ > if (!strcmp(argv[1], "start")) { > printf("Starting %s...\n", argv[3]); > >@@ -164,6 +171,23 @@ > } > printf("Stop of %s complete\n", argv[3]); > goto out; >+ } else if (!strcmp(argv[1], "migrate")) { >+ printf("Migrating %s to %s...\n", argv[3], argv[4]); >+ >+ #if 0 >+ if (!group_migratory(curres)) { >+ printf("No can do\n"); >+ ret = -1; >+ goto out; >+ } >+ #endif >+ >+ if (res_exec(rn, "migrate", argv[4], 0)) { >+ ret = -1; >+ goto out; >+ } >+ printf("Migration of %s complete\n", argv[3]); >+ goto out; > } else if (!strcmp(argv[1], "status")) { > printf("Checking status of %s...\n", argv[3]); > >Index: src/resources/Makefile >=================================================================== >RCS file: /cvs/cluster/cluster/rgmanager/src/resources/Makefile,v >retrieving revision 1.13.2.3 >diff -u -r1.13.2.3 Makefile >--- src/resources/Makefile 15 Feb 2007 22:49:19 -0000 1.13.2.3 >+++ src/resources/Makefile 10 Mar 2007 00:26:18 -0000 >@@ -20,7 +20,7 @@ > RESOURCES=fs.sh service.sh ip.sh nfsclient.sh nfsexport.sh \ > script.sh netfs.sh clusterfs.sh smb.sh \ > apache.sh openldap.sh samba.sh mysql.sh \ >- postgres-8.sh tomcat-5.sh lvm.sh >+ postgres-8.sh tomcat-5.sh lvm.sh vm.sh > > METADATA=apache.metadata openldap.metadata samba.metadata \ > mysql.metadata postgres-8.metadata tomcat-5.metadata >Index: src/resources/vm.sh >=================================================================== >RCS file: /cvs/cluster/cluster/rgmanager/src/resources/vm.sh,v >retrieving revision 1.1 >diff -u -r1.1 vm.sh >--- src/resources/vm.sh 23 Oct 2006 22:47:01 -0000 1.1 >+++ src/resources/vm.sh 10 Mar 2007 00:26:18 -0000 >@@ -367,14 +367,15 @@ > { > declare target=$1 > >- # XXX TODO >- return 1 >+ xm migrate $OCF_RESKEY_name $target >+ return $? > } > > # > # A Resource group is abstract, but the OCF RA API doesn't allow for abstract > # resources, so here it is. > # >+ > case $1 in > start) > start >Index: src/utils/clusvcadm.c >=================================================================== >RCS file: /cvs/cluster/cluster/rgmanager/src/utils/clusvcadm.c,v >retrieving revision 1.12.2.2 >diff -u -r1.12.2.2 clusvcadm.c >--- src/utils/clusvcadm.c 14 Dec 2006 22:17:44 -0000 1.12.2.2 >+++ src/utils/clusvcadm.c 10 Mar 2007 00:26:19 -0000 >@@ -161,7 +161,7 @@ > name); > printf(" %s -M <group> -m <member> Migrate <group> [to <member>]\n", > name); >-printf(" (e.g. for live migration of Xen VMs)\n"); >+printf(" (e.g. for live migration of VMs)\n"); > printf(" %s -q Quiet operation\n", name); > printf(" %s -R <group> Restart a group in place.\n", > name); >@@ -240,7 +240,7 @@ > return 1; > } > >- while ((opt = getopt(argc, argv, "lSue:d:r:n:m:vR:s:qh?")) != EOF) { >+ while ((opt = getopt(argc, argv, "lSue:M:d:r:n:m:vR:s:qh?")) != EOF) { > switch (opt) { > case 'l': > return do_lock(); >@@ -265,13 +265,13 @@ > break; > case 'r': > /* RELOCATE */ >- actionstr = "trying to relocate"; >+ actionstr = "relocate"; > action = RG_RELOCATE; > svcname = optarg; > break; > case 'M': > /* MIGRATE */ >- actionstr = "trying to migrate"; >+ actionstr = "migrate"; > action = RG_MIGRATE; > svcname = optarg; > break; >@@ -359,9 +359,9 @@ > msg_open(MSG_SOCKET, 0, RG_PORT, &ctx, 5); > } else { > if (!svctarget) >- printf("Trying to relocate %s", svcname); >+ printf("Trying to %s %s", actionstr, svcname); > else >- printf("Trying to relocate %s to %s", svcname, >+ printf("Trying to %s %s to %s", actionstr, svcname, > nodename); > printf("..."); > fflush(stdout); >@@ -392,12 +392,13 @@ > > swab_SmMessageSt(&msg); > printf("%s\n", rg_strerror(msg.sm_data.d_ret)); >- > if (msg.sm_data.d_ret == RG_ERUN) > return 0; >+ if (msg.sm_data.d_ret) >+ return msg.sm_data.d_ret; > > switch (action) { >- case RG_MIGRATE: >+ /*case RG_MIGRATE:*/ > case RG_RELOCATE: > case RG_START: > case RG_ENABLE:
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 231692
: 149757